From 375bc4cc69447bac3cd800f991201e6e6790c2ad Mon Sep 17 00:00:00 2001 From: Whatcookie Date: Thu, 27 Jul 2023 23:55:20 -0400 Subject: [PATCH 001/184] Utils: Add initial AVX10 support - Adds detection for AVX10 features - Also adds new bools for 256-wide AVX-512 instructions, indicated by either AVX-512 support, or AVX10 --- rpcs3/util/sysinfo.cpp | 81 +++++++++++++++++++++++++++++++++++++++++- rpcs3/util/sysinfo.hpp | 10 ++++++ 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/rpcs3/util/sysinfo.cpp b/rpcs3/util/sysinfo.cpp index d6aff4f78d..af44d1df50 100755 --- a/rpcs3/util/sysinfo.cpp +++ b/rpcs3/util/sysinfo.cpp @@ -177,6 +177,71 @@ bool utils::has_avx512_vnni() #endif } +bool utils::has_avx10() +{ +#if defined(ARCH_X64) + // Implies support for most AVX-512 instructions + static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && get_cpuid(7, 1)[3] & 0x80000; + return g_value; +#else + return false; +#endif +} + +bool utils::has_avx10_512() +{ +#if defined(ARCH_X64) + // AVX10 with 512 wide vectors + static const bool g_value = has_avx10() && get_cpuid(24, 0)[2] & 0x40000; + return g_value; +#else + return false; +#endif +} + +u32 utils::avx10_isa_version() +{ +#if defined(ARCH_X64) + // 8bit value + static const u32 g_value = []() + { + u32 isa_version = 0; + if (has_avx10()) + { + isa_version = get_cpuid(24, 0)[2] & 0x000ff; + } + + return isa_version; + }(); + + return g_value; +#else + return 0; +#endif +} + +bool utils::has_avx512_256() +{ +#if defined(ARCH_X64) + // Either AVX10 or AVX512 implies support for 256-bit length AVX-512 SKL-X tier instructions + static const bool g_value = (has_avx512() || has_avx10()); + return g_value; +#else + return false; +#endif +} + +bool utils::has_avx512_icl_256() +{ +#if defined(ARCH_X64) + // Check for AVX512_ICL or check for AVX10, together with GFNI, VAES, and VPCLMULQDQ, implies support for the same instructions that AVX-512_icl does at 256 bit length + static const bool g_value = (has_avx512_icl() || (has_avx10() && get_cpuid(7, 0)[2] & 0x00000700)); + return g_value; +#else + return false; +#endif +} + bool utils::has_xop() { #if defined(ARCH_X64) @@ -335,7 +400,21 @@ std::string utils::get_system_info() { result += " | AVX"; - if (has_avx512()) + if (has_avx10()) + { + const u32 avx10_version = avx10_isa_version(); + fmt::append(result, "10.%d", avx10_version); + + if (has_avx10_512()) + { + result += "-512"; + } + else + { + result += "-256"; + } + } + else if (has_avx512()) { result += "-512"; diff --git a/rpcs3/util/sysinfo.hpp b/rpcs3/util/sysinfo.hpp index 951c961bb6..39826af5a8 100755 --- a/rpcs3/util/sysinfo.hpp +++ b/rpcs3/util/sysinfo.hpp @@ -27,6 +27,16 @@ namespace utils bool has_avx512_vnni(); + bool has_avx10(); + + bool has_avx10_512(); + + u32 avx10_isa_version(); + + bool has_avx512_256(); + + bool has_avx512_icl_256(); + bool has_xop(); bool has_clwb(); From 4ecb06c901effffa28850f74a9ed39e66f3a9b6b Mon Sep 17 00:00:00 2001 From: Whatcookie Date: Fri, 28 Jul 2023 03:26:40 -0400 Subject: [PATCH 002/184] SPU LLVM: Optimize common SFI+ROTQMBY pattern --- rpcs3/Emu/Cell/SPURecompiler.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index 6df6380485..1dd9acab29 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -7788,19 +7788,27 @@ public: void ROTQMBY(spu_opcode_t op) { const auto a = get_vr(op.ra); - const auto b = get_vr(op.rb); + const auto b = get_vr(op.rb); + + auto minusb = eval(-b); + if (auto [ok, x] = match_expr(b, -match()); ok) + { + minusb = eval(x); + } + + const auto minusbx = bitcast(minusb); // Data with swapped endian from a load instruction if (auto [ok, as] = match_expr(a, byteswap(match())); ok) { const auto sc = build(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0); - const auto sh = sc - (-splat_scalar(b) & 0x1f); + const auto sh = sc - (splat_scalar(minusbx) & 0x1f); set_vr(op.rt, pshufb(as, sh)); return; } const auto sc = build(112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127); - const auto sh = sc + (-splat_scalar(b) & 0x1f); + const auto sh = sc + (splat_scalar(minusbx) & 0x1f); set_vr(op.rt, pshufb(a, sh)); } From dabb2cc9a08fd62142a6502050ca59a73810a57b Mon Sep 17 00:00:00 2001 From: Talkashie Date: Fri, 28 Jul 2023 05:09:06 -0500 Subject: [PATCH 003/184] Fix typos, improve consistency Fixes typos where spelling or grammar is objectively wrong. Changes wording and capitalization in some areas to be more consistent with other areas. --- rpcs3/Emu/Audio/AudioBackend.cpp | 2 +- rpcs3/Emu/Audio/Cubeb/CubebBackend.cpp | 2 +- rpcs3/Emu/Cell/Modules/cellHttpUtil.cpp | 2 +- rpcs3/Emu/Cell/PPUThread.cpp | 2 +- rpcs3/Emu/Cell/SPUThread.cpp | 2 +- rpcs3/Emu/NP/rpcn_client.cpp | 6 ++-- rpcs3/Emu/RSX/GL/glutils/capabilities.cpp | 2 +- rpcs3/Emu/RSX/VK/VKGSRender.cpp | 8 +++--- rpcs3/Emu/RSX/VK/vkutils/device.cpp | 2 +- rpcs3/Emu/System.cpp | 8 +++--- rpcs3/Emu/savestate_utils.cpp | 4 +-- rpcs3/Input/hid_pad_handler.cpp | 2 +- rpcs3/rpcs3qt/cheat_manager.cpp | 2 +- rpcs3/rpcs3qt/debugger_frame.cpp | 2 +- rpcs3/rpcs3qt/emu_settings.cpp | 2 +- rpcs3/rpcs3qt/localized_emu.h | 4 +-- rpcs3/rpcs3qt/main_window.cpp | 4 +-- rpcs3/rpcs3qt/midi_creator.cpp | 6 ++-- rpcs3/rpcs3qt/rpcn_settings_dialog.cpp | 34 +++++++++++------------ rpcs3/rpcs3qt/settings_dialog.ui | 14 +++++----- rpcs3/rpcs3qt/tooltips.h | 26 ++++++++--------- 21 files changed, 68 insertions(+), 68 deletions(-) diff --git a/rpcs3/Emu/Audio/AudioBackend.cpp b/rpcs3/Emu/Audio/AudioBackend.cpp index d7d03658e1..320ec753bc 100644 --- a/rpcs3/Emu/Audio/AudioBackend.cpp +++ b/rpcs3/Emu/Audio/AudioBackend.cpp @@ -158,6 +158,6 @@ AudioChannelCnt AudioBackend::convert_channel_count(u64 raw) case 1: return AudioChannelCnt::STEREO; case 0: - fmt::throw_exception("Usupported channel count"); + fmt::throw_exception("Unsupported channel count"); } } diff --git a/rpcs3/Emu/Audio/Cubeb/CubebBackend.cpp b/rpcs3/Emu/Audio/Cubeb/CubebBackend.cpp index 4c89ff8fbc..0ca8f0b1ea 100644 --- a/rpcs3/Emu/Audio/Cubeb/CubebBackend.cpp +++ b/rpcs3/Emu/Audio/Cubeb/CubebBackend.cpp @@ -543,7 +543,7 @@ void CubebBackend::device_collection_changed_cb(cubeb* context, void* user_ptr) if (context != cubeb->m_ctx) { - Cubeb.error("device_collection_changed_cb called with unkown context"); + Cubeb.error("device_collection_changed_cb called with unknown context"); return; } diff --git a/rpcs3/Emu/Cell/Modules/cellHttpUtil.cpp b/rpcs3/Emu/Cell/Modules/cellHttpUtil.cpp index e57872679e..1d1cd2fd8e 100644 --- a/rpcs3/Emu/Cell/Modules/cellHttpUtil.cpp +++ b/rpcs3/Emu/Cell/Modules/cellHttpUtil.cpp @@ -132,7 +132,7 @@ error_code cellHttpUtilParseUri(vm::ptr uri, vm::cptr str, vm parseError = "Error, URI didn't contain a slash"; break; default: - parseError = "Error, unkown error #" + std::to_string(static_cast(URL.m_ErrorCode)); + parseError = "Error, unknown error #" + std::to_string(static_cast(URL.m_ErrorCode)); break; } cellHttpUtil.error("%s, while parsing URI, %s.", parseError, str.get_ptr()); diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index c50e8c1b1a..4b09778a00 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -1952,7 +1952,7 @@ void ppu_thread::exec_task() ppu_thread::~ppu_thread() { - perf_log.notice("Perf stats for STCX reload: successs %u, failure %u", last_succ, last_fail); + perf_log.notice("Perf stats for STCX reload: success %u, failure %u", last_succ, last_fail); perf_log.notice("Perf stats for instructions: total %u", exec_bytes / 4); } diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 7a36b46565..f24f41ef3a 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -1821,7 +1821,7 @@ spu_thread::~spu_thread() utils::memory_release(ls - SPU_LS_SIZE * 2, SPU_LS_SIZE * 5); perf_log.notice("Perf stats for transactions: success %u, failure %u", stx, ftx); - perf_log.notice("Perf stats for PUTLLC reload: successs %u, failure %u", last_succ, last_fail); + perf_log.notice("Perf stats for PUTLLC reload: success %u, failure %u", last_succ, last_fail); } u8* spu_thread::map_ls(utils::shm& shm, void* ptr) diff --git a/rpcs3/Emu/NP/rpcn_client.cpp b/rpcs3/Emu/NP/rpcn_client.cpp index 9a46c73a1e..41bc86c698 100644 --- a/rpcs3/Emu/NP/rpcn_client.cpp +++ b/rpcs3/Emu/NP/rpcn_client.cpp @@ -312,7 +312,7 @@ namespace rpcn *utils::bless>(&ping[9]) = local_addr_sig; if (send_packet_from_p2p_port(ping, addr_rpcn_udp) == -1) { - rpcn_log.error("Failed to send ping to rpcn!"); + rpcn_log.error("Failed to send ping to RPCN!"); } last_ping_time = now; } @@ -397,7 +397,7 @@ namespace rpcn } else { - rpcn_log.error("Tried to forward a reply whose packet_id marks it as internal to rpcn"); + rpcn_log.error("Tried to forward a reply whose packet_id marks it as internal to RPCN"); } } @@ -2126,7 +2126,7 @@ namespace rpcn if (!fb_mdata->communicationId() || fb_mdata->communicationId()->size() == 0 || fb_mdata->communicationId()->size() > 9 || !fb_mdata->subject() || !fb_mdata->body() || !fb_mdata->data()) { - rpcn_log.warning("Discarded invalid messaged!"); + rpcn_log.warning("Discarded invalid message!"); return; } diff --git a/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp b/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp index df92d7b920..3bc9cd37f2 100644 --- a/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp +++ b/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp @@ -39,7 +39,7 @@ namespace gl if (!ext_count) { - rsx_log.error("Coult not initialize GL driver capabilities. Is OpenGL initialized?"); + rsx_log.error("Could not initialize GL driver capabilities. Is OpenGL initialized?"); return; } diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index 56a38e6345..0cff656119 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -529,7 +529,7 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar) } else { - rsx_log.fatal("Could not find a vulkan compatible GPU driver. Your GPU(s) may not support Vulkan, or you need to install the vulkan runtime and drivers"); + rsx_log.fatal("Could not find a Vulkan compatible GPU driver. Your GPU(s) may not support Vulkan, or you need to install the Vulkan runtime and drivers"); m_device = VK_NULL_HANDLE; return; } @@ -799,7 +799,7 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar) #endif if (backend_config.supports_passthrough_dma) { - rsx_log.error("AMDGPU kernel driver on linux and INTEL driver on some platforms cannot support passthrough DMA buffers."); + rsx_log.error("AMDGPU kernel driver on Linux and INTEL driver on some platforms cannot support passthrough DMA buffers."); backend_config.supports_passthrough_dma = false; } break; @@ -1442,7 +1442,7 @@ void VKGSRender::on_init_thread() { if (m_device == VK_NULL_HANDLE) { - fmt::throw_exception("No vulkan device was created"); + fmt::throw_exception("No Vulkan device was created"); } GSRender::on_init_thread(); @@ -3010,7 +3010,7 @@ void VKGSRender::begin_conditional_rendering(const std::vectorpending); + rsx_log.warning("Dubious query data pushed to cond render! Please report to developers(q.pending=%d)", sources.front()->pending); } rsx::thread::begin_conditional_rendering(sources); diff --git a/rpcs3/Emu/RSX/VK/vkutils/device.cpp b/rpcs3/Emu/RSX/VK/vkutils/device.cpp index 2b56591d58..f2f1673d0c 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/device.cpp +++ b/rpcs3/Emu/RSX/VK/vkutils/device.cpp @@ -207,7 +207,7 @@ namespace vk get_physical_device_features(allow_extensions); get_physical_device_properties(allow_extensions); - rsx_log.always()("Found vulkan-compatible GPU: '%s' running on driver %s", get_name(), get_driver_version()); + rsx_log.always()("Found Vulkan-compatible GPU: '%s' running on driver %s", get_name(), get_driver_version()); if (get_driver_vendor() == driver_vendor::RADV && get_name().find("LLVM 8.0.0") != umax) { diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 9eff4d8162..f4211e2c62 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -128,7 +128,7 @@ void fmt_class_string::format(std::string& out, u64 arg) case game_boot_result::firmware_missing: return "Firmware is missing"; case game_boot_result::unsupported_disc_type: return "This disc type is not supported yet"; case game_boot_result::savestate_corrupted: return "Savestate data is corrupted or it's not an RPCS3 savestate"; - case game_boot_result::savestate_version_unsupported: return "Savestate versioning data differes from your RPCS3 build"; + case game_boot_result::savestate_version_unsupported: return "Savestate versioning data differs from your RPCS3 build"; case game_boot_result::still_running: return "Game is still running"; } return unknown; @@ -2576,9 +2576,9 @@ void Emulator::Kill(bool allow_autoexit, bool savestate) try_lock_spu_threads_in_a_state_compatible_with_savestates(true); sys_log.error("Failed to savestate: HLE VDEC (video decoder) context(s) exist." - "\nLLE libvdec.sprx by selecting it in Adavcned tab -> Firmware Libraries." - "\nYou need to close the game for to take effect." - "\nIf you cannot close the game due to losing important progress your best chance is to skip the current cutscenes if any are played and retry."); + "\nLLE libvdec.sprx by selecting it in Advanced tab -> Firmware Libraries." + "\nYou need to close the game for it to take effect." + "\nIf you cannot close the game due to losing important progress, your best chance is to skip the current cutscenes if any are played and retry."); return; } diff --git a/rpcs3/Emu/savestate_utils.cpp b/rpcs3/Emu/savestate_utils.cpp index e76bff203f..9b423ed415 100644 --- a/rpcs3/Emu/savestate_utils.cpp +++ b/rpcs3/Emu/savestate_utils.cpp @@ -121,7 +121,7 @@ bool is_savestate_version_compatible(const std::vector>& dat { if (identifier >= s_serial_versions.size()) { - (is_boot_check ? sys_log.error : sys_log.trace)("Savestate version identider is unknown! (category=%u, version=%u)", identifier, version); + (is_boot_check ? sys_log.error : sys_log.trace)("Savestate version identifier is unknown! (category=%u, version=%u)", identifier, version); ok = false; // Log all mismatches } else if (!s_serial_versions[identifier].compatible_versions.count(version)) @@ -234,7 +234,7 @@ bool boot_last_savestate(bool testing) if (game_boot_result error = Emu.BootGame(savestate_path, "", true); error != game_boot_result::no_errors) { - sys_log.error("Failed to booting savestate \'%s\' using the Reload shortcut. (error: %s)", savestate_path, error); + sys_log.error("Failed to boot savestate \'%s\' using the Reload shortcut. (error: %s)", savestate_path, error); } else { diff --git a/rpcs3/Input/hid_pad_handler.cpp b/rpcs3/Input/hid_pad_handler.cpp index 0ed9a06d3e..f31c12e4c5 100644 --- a/rpcs3/Input/hid_pad_handler.cpp +++ b/rpcs3/Input/hid_pad_handler.cpp @@ -214,7 +214,7 @@ void hid_pad_handler::update_devices() { hid_log.error("One or more %s pads were detected but couldn't be interacted with directly", m_type); #if defined(_WIN32) || defined(__linux__) - hid_log.error("Check https://wiki.rpcs3.net/index.php?title=Help:Controller_Configuration for intructions on how to solve this issue"); + hid_log.error("Check https://wiki.rpcs3.net/index.php?title=Help:Controller_Configuration for instructions on how to solve this issue"); #endif } else diff --git a/rpcs3/rpcs3qt/cheat_manager.cpp b/rpcs3/rpcs3qt/cheat_manager.cpp index 218522be0e..36f797c932 100644 --- a/rpcs3/rpcs3qt/cheat_manager.cpp +++ b/rpcs3/rpcs3qt/cheat_manager.cpp @@ -852,7 +852,7 @@ cheat_manager_dialog::cheat_manager_dialog(QWidget* parent) { if (g_cheat.exist(name, offset)) { - if (QMessageBox::question(this, tr("Cheat already exist"), tr("Do you want to overwrite the existing cheat?"), QMessageBox::Ok | QMessageBox::Cancel) != QMessageBox::Ok) + if (QMessageBox::question(this, tr("Cheat already exists"), tr("Do you want to overwrite the existing cheat?"), QMessageBox::Ok | QMessageBox::Cancel) != QMessageBox::Ok) return; } diff --git a/rpcs3/rpcs3qt/debugger_frame.cpp b/rpcs3/rpcs3qt/debugger_frame.cpp index b529e38359..d8131562c5 100644 --- a/rpcs3/rpcs3qt/debugger_frame.cpp +++ b/rpcs3/rpcs3qt/debugger_frame.cpp @@ -699,7 +699,7 @@ void debugger_frame::keyPressEvent(QKeyEvent* event) if (!cpu->state.all_of(cpu_flag::wait + cpu_flag::dbg_pause)) { - QMessageBox::warning(this, QObject::tr("Pause the SPU Thread!"), QObject::tr("Cannot perform SPU capture due to the thread need manual pausing!")); + QMessageBox::warning(this, QObject::tr("Pause the SPU Thread!"), QObject::tr("Cannot perform SPU capture due to the thread needing manual pausing!")); return; } diff --git a/rpcs3/rpcs3qt/emu_settings.cpp b/rpcs3/rpcs3qt/emu_settings.cpp index 871fdf5f9c..37938a9738 100644 --- a/rpcs3/rpcs3qt/emu_settings.cpp +++ b/rpcs3/rpcs3qt/emu_settings.cpp @@ -1011,7 +1011,7 @@ QString emu_settings::GetLocalizedSetting(const QString& original, emu_settings_ case camera_flip::none: return tr("No", "Camera flip"); case camera_flip::horizontal: return tr("Flip horizontally", "Camera flip"); case camera_flip::vertical: return tr("Flip vertically", "Camera flip"); - case camera_flip::both: return tr("Flip both axis", "Camera flip"); + case camera_flip::both: return tr("Flip both axes", "Camera flip"); } break; case emu_settings_type::Camera: diff --git a/rpcs3/rpcs3qt/localized_emu.h b/rpcs3/rpcs3qt/localized_emu.h index 9553c6fefd..0fbcb00d62 100644 --- a/rpcs3/rpcs3qt/localized_emu.h +++ b/rpcs3/rpcs3qt/localized_emu.h @@ -86,7 +86,7 @@ private: case localized_string_id::CELL_MSG_DIALOG_ERROR_80010004: return tr("Memory allocation failed.\n(%0)", "Error code").arg(std::forward(args)...); case localized_string_id::CELL_MSG_DIALOG_ERROR_80010005: return tr("The resource with the specified identifier does not exist.\n(%0)", "Error code").arg(std::forward(args)...); case localized_string_id::CELL_MSG_DIALOG_ERROR_80010006: return tr("The file does not exist.\n(%0)", "Error code").arg(std::forward(args)...); - case localized_string_id::CELL_MSG_DIALOG_ERROR_80010007: return tr("The file is in unrecognized format / The file is not a valid ELF file.\n(%0)", "Error code").arg(std::forward(args)...); + case localized_string_id::CELL_MSG_DIALOG_ERROR_80010007: return tr("The file is in an unrecognized format / The file is not a valid ELF file.\n(%0)", "Error code").arg(std::forward(args)...); case localized_string_id::CELL_MSG_DIALOG_ERROR_80010008: return tr("Resource deadlock is avoided.\n(%0)", "Error code").arg(std::forward(args)...); case localized_string_id::CELL_MSG_DIALOG_ERROR_80010009: return tr("Operation not permitted.\n(%0)", "Error code").arg(std::forward(args)...); case localized_string_id::CELL_MSG_DIALOG_ERROR_8001000A: return tr("The device or resource is busy.\n(%0)", "Error code").arg(std::forward(args)...); @@ -135,7 +135,7 @@ private: case localized_string_id::CELL_MSG_DIALOG_ERROR_80010036: return tr("Not empty.\n(%0)", "Error code").arg(std::forward(args)...); case localized_string_id::CELL_MSG_DIALOG_ERROR_80010037: return tr("Not supported.\n(%0)", "Error code").arg(std::forward(args)...); case localized_string_id::CELL_MSG_DIALOG_ERROR_80010038: return tr("File-system specific error.\n(%0)", "Error code").arg(std::forward(args)...); - case localized_string_id::CELL_MSG_DIALOG_ERROR_80010039: return tr("Overflow occured.\n(%0)", "Error code").arg(std::forward(args)...); + case localized_string_id::CELL_MSG_DIALOG_ERROR_80010039: return tr("Overflow occurred.\n(%0)", "Error code").arg(std::forward(args)...); case localized_string_id::CELL_MSG_DIALOG_ERROR_8001003A: return tr("Filesystem not mounted.\n(%0)", "Error code").arg(std::forward(args)...); case localized_string_id::CELL_MSG_DIALOG_ERROR_8001003B: return tr("Not SData.\n(%0)", "Error code").arg(std::forward(args)...); case localized_string_id::CELL_MSG_DIALOG_ERROR_8001003C: return tr("Incorrect version in sys_load_param.\n(%0)", "Error code").arg(std::forward(args)...); diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 71e897a54b..25f7369a83 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -445,7 +445,7 @@ void main_window::show_boot_error(game_boot_result status) message = tr("Savestate data is corrupted or it's not an RPCS3 savestate."); break; case game_boot_result::savestate_version_unsupported: - message = tr("Savestate versioning data differes from your RPCS3 build."); + message = tr("Savestate versioning data differs from your RPCS3 build."); break; case game_boot_result::still_running: message = tr("A game or PS3 application is still running or has yet to be fully stopped."); @@ -1317,7 +1317,7 @@ void main_window::ExtractTar() if (!error.isEmpty()) { pdlg.hide(); - QMessageBox::critical(this, tr("Tar extraction failed"), error); + QMessageBox::critical(this, tr("TAR extraction failed"), error); } } diff --git a/rpcs3/rpcs3qt/midi_creator.cpp b/rpcs3/rpcs3qt/midi_creator.cpp index f7c5ef1816..ff83461fec 100644 --- a/rpcs3/rpcs3qt/midi_creator.cpp +++ b/rpcs3/rpcs3qt/midi_creator.cpp @@ -15,7 +15,7 @@ midi_creator::midi_creator() // We need to recreate the localized string because the midi creator is currently only created once. QString midi_creator::get_none() { - return tr("None", "Midi device"); + return tr("None", "MIDI device"); } void midi_creator::refresh_list() @@ -49,11 +49,11 @@ void midi_creator::refresh_list() s32 size = sizeof(buf); if (rtmidi_get_port_name(midi_in.get(), port_number, buf, &size) == -1) { - cfg_log.error("Error getting midi port name for port %d: %s", port_number, midi_in->msg); + cfg_log.error("Error getting MIDI port name for port %d: %s", port_number, midi_in->msg); continue; } - cfg_log.notice("Found midi device with name: %s", buf); + cfg_log.notice("Found MIDI device with name: %s", buf); m_midi_list.append(QString::fromUtf8(buf)); } } diff --git a/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp b/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp index 2dff073574..106f60cee1 100644 --- a/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp @@ -257,10 +257,10 @@ rpcn_account_dialog::rpcn_account_dialog(QWidget* parent) case rpcn::ErrorType::CreationExistingUsername: error_message = tr("An account with that username already exists!"); break; case rpcn::ErrorType::CreationBannedEmailProvider: error_message = tr("This email provider is banned!"); break; case rpcn::ErrorType::CreationExistingEmail: error_message = tr("An account with that email already exists!"); break; - case rpcn::ErrorType::CreationError: error_message = tr("Unknown creation error"); break; + case rpcn::ErrorType::CreationError: error_message = tr("Unknown creation error!"); break; default: error_message = tr("Unknown error"); break; } - QMessageBox::critical(this, tr("Error Creating Account"), tr("Failed to create the account:\n%0").arg(error_message), QMessageBox::Ok); + QMessageBox::critical(this, tr("Error Creating Account!"), tr("Failed to create the account:\n%0").arg(error_message), QMessageBox::Ok); return; } } @@ -361,12 +361,12 @@ rpcn_add_server_dialog::rpcn_add_server_dialog(QWidget* parent) if (description.isEmpty()) { - QMessageBox::critical(this, tr("Missing Description"), tr("You must enter a description!"), QMessageBox::Ok); + QMessageBox::critical(this, tr("Missing Description!"), tr("You must enter a description!"), QMessageBox::Ok); return; } if (host.isEmpty()) { - QMessageBox::critical(this, tr("Missing Hostname"), tr("You must enter a hostname for the server!"), QMessageBox::Ok); + QMessageBox::critical(this, tr("Missing Hostname!"), tr("You must enter a hostname for the server!"), QMessageBox::Ok); return; } @@ -413,12 +413,12 @@ rpcn_ask_username_dialog::rpcn_ask_username_dialog(QWidget* parent, const QStrin if (username.empty()) { - QMessageBox::critical(this, tr("Missing Username"), tr("You must enter a username!"), QMessageBox::Ok); + QMessageBox::critical(this, tr("Missing Username!"), tr("You must enter a username!"), QMessageBox::Ok); return; } if (!validate_rpcn_username(username)) { - QMessageBox::critical(this, tr("Invalid Username"), tr("Please enter a valid username!"), QMessageBox::Ok); + QMessageBox::critical(this, tr("Invalid Username!"), tr("Please enter a valid username!"), QMessageBox::Ok); } m_username = username; @@ -729,7 +729,7 @@ void rpcn_account_edit_dialog::resend_token() if (auto result = rpcn->wait_for_connection(); result != rpcn::rpcn_state::failure_no_failure) { const QString error_message = tr("Failed to connect to RPCN server:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(result))); - QMessageBox::critical(this, tr("Error Connecting"), error_message, QMessageBox::Ok); + QMessageBox::critical(this, tr("Error Connecting!"), error_message, QMessageBox::Ok); return; } @@ -745,7 +745,7 @@ void rpcn_account_edit_dialog::resend_token() case rpcn::ErrorType::LoginError: error_message = tr("The username/password pair is invalid!"); break; default: error_message = tr("Unknown error"); break; } - QMessageBox::critical(this, tr("Error Sending Token"), tr("Failed to send the token:\n%0").arg(error_message), QMessageBox::Ok); + QMessageBox::critical(this, tr("Error Sending Token!"), tr("Failed to send the token:\n%0").arg(error_message), QMessageBox::Ok); return; } @@ -778,7 +778,7 @@ void rpcn_account_edit_dialog::change_password() if (auto result = rpcn->wait_for_connection(); result != rpcn::rpcn_state::failure_no_failure) { const QString error_message = tr("Failed to connect to RPCN server:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(result))); - QMessageBox::critical(this, tr("Error Connecting"), error_message, QMessageBox::Ok); + QMessageBox::critical(this, tr("Error Connecting!"), error_message, QMessageBox::Ok); return; } @@ -792,9 +792,9 @@ void rpcn_account_edit_dialog::change_password() case rpcn::ErrorType::TooSoon: error_message = tr("You can only ask for a reset password token once every 24 hours!"); break; case rpcn::ErrorType::EmailFail: error_message = tr("The mail couldn't be sent successfully!"); break; case rpcn::ErrorType::LoginError: error_message = tr("The username/email pair is invalid!"); break; - default: error_message = tr("Unknown error"); break; + default: error_message = tr("Unknown error!"); break; } - QMessageBox::critical(this, tr("Error Sending Password Reset Token"), tr("Failed to send the password reset token:\n%0").arg(error_message), QMessageBox::Ok); + QMessageBox::critical(this, tr("Error Sending Password Reset Token!"), tr("Failed to send the password reset token:\n%0").arg(error_message), QMessageBox::Ok); return; } @@ -822,7 +822,7 @@ void rpcn_account_edit_dialog::change_password() if (auto result = rpcn->wait_for_connection(); result != rpcn::rpcn_state::failure_no_failure) { const QString error_message = tr("Failed to connect to RPCN server:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(result))); - QMessageBox::critical(this, tr("Error Connecting"), error_message, QMessageBox::Ok); + QMessageBox::critical(this, tr("Error Connecting!"), error_message, QMessageBox::Ok); return; } @@ -964,7 +964,7 @@ rpcn_friends_dialog::rpcn_friends_dialog(QWidget* parent) { if (!m_rpcn->remove_friend(str_sel_friend)) { - QMessageBox::critical(this, tr("Error removing a friend!"), tr("An error occured trying to remove a friend!"), QMessageBox::Ok); + QMessageBox::critical(this, tr("Error removing a friend!"), tr("An error occurred while trying to remove a friend!"), QMessageBox::Ok); } else { @@ -1000,7 +1000,7 @@ rpcn_friends_dialog::rpcn_friends_dialog(QWidget* parent) { if (!m_rpcn->add_friend(str_sel_friend)) { - QMessageBox::critical(this, tr("Error adding a friend!"), tr("An error occured trying to add a friend!"), QMessageBox::Ok); + QMessageBox::critical(this, tr("Error adding a friend!"), tr("An error occurred while trying to add a friend!"), QMessageBox::Ok); } else { @@ -1031,17 +1031,17 @@ rpcn_friends_dialog::rpcn_friends_dialog(QWidget* parent) break; } - QMessageBox::critical(this, tr("Error validating username"), tr("The username you entered is invalid"), QMessageBox::Ok); + QMessageBox::critical(this, tr("Error validating username!"), tr("The username you entered is invalid!"), QMessageBox::Ok); } if (!m_rpcn->add_friend(str_friend_username)) { - QMessageBox::critical(this, tr("Error adding friend"), tr("An error occured adding friend"), QMessageBox::Ok); + QMessageBox::critical(this, tr("Error adding friend!"), tr("An error occurred while adding a friend!"), QMessageBox::Ok); } else { add_update_list(m_lst_requests, QString::fromStdString(str_friend_username), m_orange_icon, QVariant(false)); - QMessageBox::information(this, tr("Friend added"), tr("Friend was successfully added!"), QMessageBox::Ok); + QMessageBox::information(this, tr("Friend added!"), tr("Friend was successfully added!"), QMessageBox::Ok); } }); diff --git a/rpcs3/rpcs3qt/settings_dialog.ui b/rpcs3/rpcs3qt/settings_dialog.ui index e96cac9ac6..dd9c67dd7a 100644 --- a/rpcs3/rpcs3qt/settings_dialog.ui +++ b/rpcs3/rpcs3qt/settings_dialog.ui @@ -1590,7 +1590,7 @@ - Guitar Hero Live emulated guitar + Guitar Hero Live Emulated Guitar @@ -1614,7 +1614,7 @@ - DJ Hero emulated turntable + DJ Hero Emulated Turntable @@ -1638,7 +1638,7 @@ - Buzz! emulated controller + Buzz! Emulated Controller @@ -1722,7 +1722,7 @@ - Emulated Midi device 1 + Emulated MIDI Device 1 @@ -1741,7 +1741,7 @@ - Emulated Midi device 3 + Emulated MIDI Device 3 @@ -1760,7 +1760,7 @@ - Emulated Midi device 2 + Emulated MIDI Device 2 @@ -2040,7 +2040,7 @@ - Disk cache + Disk Cache diff --git a/rpcs3/rpcs3qt/tooltips.h b/rpcs3/rpcs3qt/tooltips.h index 638fc6a289..7850177f9e 100644 --- a/rpcs3/rpcs3qt/tooltips.h +++ b/rpcs3/rpcs3qt/tooltips.h @@ -49,13 +49,13 @@ public: const QString wake_up_delay = tr("Controls how much time it takes for RSX to start processing after waking up by the Cell processor.\nIncreasing wakeup delay improves stability, but very high values can lower RSX/GPU performance.\nIt is recommend to adjust this at 20µs to 40µs increments until the best value for optimal stability is reached."); const QString disabled_from_global = tr("Do not change this setting globally.\nRight-click a game in the game list and choose \"Configure\" instead."); const QString vulkan_async_scheduler = tr("Determines how to schedule GPU async compute jobs when using asynchronous streaming.\nUse 'Safe' mode for more spec compliant behavior at the cost of some CPU overhead. This setting works with all devices.\nUse 'Fast' to use a faster but hacky version. This option is internally disabled for NVIDIA GPUs due to causing GPU hangs."); - const QString disable_msl_fast_math = tr("Disables Fast Math for MSL shaders, which may violate the IEEE 754 standard.\nDisabling it may fix some artefacts especially on Apple GPUs, at the cost of performance."); - const QString suspend_savestates = tr("When this mode is on, emulation exits when saving and the savestate file is concealed after loading it, preventing reuse by RPCS3.\nThis mode is like hibernation of emulation: if you don't want to be able to cheat using savestates when playing the game, consider using this mode.\nDo note that the savestate file is not gone completely just ignored by RPCS3, you can manually relaunch it if needed."); + const QString disable_msl_fast_math = tr("Disables Fast Math for MSL shaders, which may violate the IEEE 754 standard.\nDisabling it may fix some artifacts, especially on Apple GPUs, at the cost of performance."); + const QString suspend_savestates = tr("When this mode is on, emulation exits when saving and the savestate file is concealed after loading it, preventing reuse by RPCS3.\nThis mode is like hibernation of emulation: if you don't want to be able to cheat using savestates when playing the game, consider using this mode.\nDo note that the savestate file is not gone completely, just ignored by RPCS3. You can manually relaunch it if needed."); const QString paused_savestates = tr("When this mode is on, savestates are loaded and paused on the first frame.\nThis allows players to prepare for gameplay without being thrown into the action immediately."); // audio - const QString audio_out = tr("Cubeb uses a cross-platform approach and supports audio buffering, so it is the recommended option.\nXAudio2 uses native Windows sounds system, is the next best alternative."); + const QString audio_out = tr("Cubeb uses a cross-platform approach and supports audio buffering, so it is the recommended option.\nXAudio2 uses native Windows sounds system and is the next best alternative."); const QString audio_out_linux = tr("Cubeb uses a cross-platform approach and supports audio buffering, so it is the recommended option.\nIf it's not available, FAudio could be used instead."); const QString audio_provider = tr("Controls which PS3 audio API is used.\nGames use CellAudio, while VSH requires RSXAudio."); const QString audio_avport = tr("Controls which avport is used to sample audio data from."); @@ -86,7 +86,7 @@ public: const QString enable_tsx = tr("Enable usage of TSX instructions.\nNeeds to be forced on some Haswell or Broadwell CPUs or CPUs with the TSX-FA instruction set.\nForcing TSX in these cases may lead to system and performance instability, use it with caution."); const QString spu_block_size = tr("This option controls the SPU analyser, particularly the size of compiled units. The Mega and Giga modes may improve performance by tying smaller units together, decreasing the number of compiled units but increasing their size.\nUse the Safe mode for maximum compatibility."); const QString preferred_spu_threads = tr("Some SPU stages are sensitive to race conditions and allowing a limited number at a time helps alleviate performance stalls.\nSetting this to a smaller value might improve performance and reduce stuttering in some games.\nLeave this on auto if performance is negatively affected when setting a small value."); - const QString max_cpu_preempt = tr("Reduces CPU usage and power consumption, on mobile devices improves battery life. (0 means disabled)\nHigher values cause a more pronounced effect, but may cause audio or performance issues. A value of 50 or less is recommended.\nThis option forces an FPS limit because it's active when framerate is stable.\nThe lighter the game is on the hardware, the more power is saved by it. (until the preemption count barrier is reached)"); + const QString max_cpu_preempt = tr("Reduces CPU usage and power consumption, improving battery life on mobile devices. (0 means disabled)\nHigher values cause a more pronounced effect, but may cause audio or performance issues. A value of 50 or less is recommended.\nThis option forces an FPS limit because it's active when framerate is stable.\nThe lighter the game is on the hardware, the more power is saved by it. (until the preemption count barrier is reached)"); // debug @@ -102,7 +102,7 @@ public: const QString accurate_cache_line_stores = tr("Accurately processes PPU DCBZ instruction.\nIn addition, when combined with Accurate SPU DMA, SPU PUT cache line accesses will be processed atomically."); const QString mfc_delay_command = tr("Forces delaying any odd MFC command, waits for at least 2 pending commands to execute them in a random order.\nMust be used with either SPU interpreters currently.\nSeverely degrades performance! If unsure, don't use this option."); const QString hook_static_functions = tr("Allows to hook some functions like 'memcpy' replacing them with high-level implementations. May do nothing or break things. Experimental."); - const QString renderdoc_compatibility = tr("Enables use of classic OpenGL buffers which allows capturing tools to work with RPCS3 e.g RenderDoc.\nAlso allows vulkan to use debug markers for nicer Renderdoc captures.\nIf unsure, don't use this option."); + const QString renderdoc_compatibility = tr("Enables use of classic OpenGL buffers which allows capturing tools to work with RPCS3 e.g RenderDoc.\nAlso allows Vulkan to use debug markers for nicer Renderdoc captures.\nIf unsure, don't use this option."); const QString force_high_pz = tr("Only useful when debugging differences in GPU hardware.\nNot necessary for average users.\nIf unsure, don't use this option."); const QString debug_output = tr("Enables the selected API's inbuilt debugging functionality.\nWill cause severe performance degradation especially with Vulkan.\nOnly useful to developers.\nIf unsure, don't use this option."); const QString debug_overlay = tr("Provides a graphical overlay of various debugging information.\nIf unsure, don't use this option."); @@ -132,7 +132,7 @@ public: const QString disable_kb_hotkeys = tr("Disables keyboard hotkeys such as Ctrl+S, Ctrl+E, Ctrl+R, Ctrl+P while the game screen is active.\nThis does not include Ctrl+L (hide and lock mouse) and Alt+Enter (toggle fullscreen).\nCheck this if you want to play with mouse and keyboard."); const QString max_llvm_threads = tr("Limits the maximum number of threads used for the initial PPU and SPU module compilation.\nLower this in order to increase performance of other open applications.\nThe default uses all available threads."); const QString show_mouse_in_fullscreen = tr("Shows the mouse cursor when the fullscreen mode is active.\nCurrently this may not work every time."); - const QString lock_mouse_in_fullscreen = tr("Locks the mouse cursor at center when the fullscreen mode is active."); + const QString lock_mouse_in_fullscreen = tr("Locks the mouse cursor to the center when the fullscreen mode is active."); const QString hide_mouse_on_idle = tr("Hides the mouse cursor if no mouse movement is detected for the configured time."); const QString show_shader_compilation_hint = tr("Shows 'Compiling shaders' hint using the native overlay."); const QString show_ppu_compilation_hint = tr("Shows 'Compiling PPU modules' hint using the native overlay."); @@ -164,13 +164,13 @@ public: const QString resolution = tr("This setting will be ignored if the Resolution Scale is set to anything other than 100%!\nLeave this on 1280x720. Every PS3 game is compatible with this resolution.\nOnly use 1920x1080 if the game supports it.\nRarely due to emulation bugs some games will only render at low resolutions like 480p."); const QString graphics_adapter = tr("On multi GPU systems select which GPU to use in RPCS3 when using Vulkan.\nThis is not needed when using OpenGL."); const QString aspect_ratio = tr("Leave this on 16:9 unless you have a 4:3 monitor."); - const QString frame_limit = tr("Off is the fastest option.\nUsing the frame limiter will add extra overhead and slow down the game. However, some games will crash if the frame rate is too high.\nPS3 native should only be used if Auto is not working correctly as it can introduce frame-pacing issues.\nInfinite adds a positive feedback loop which adds another vblank signal per frame allowing more games to be fps limitless."); + const QString frame_limit = tr("Off is the fastest option.\nUsing the frame limiter will add extra overhead and slow down the game. However, some games will crash if the framerate is too high.\nPS3 native should only be used if Auto is not working correctly as it can introduce frame-pacing issues.\nInfinite adds a positive feedback loop which adds another vblank signal per frame allowing more games to be fps limitless."); const QString anti_aliasing = tr("Emulate PS3 multisampling layout.\nCan fix some otherwise difficult to solve graphics glitches.\nLow to moderate performance hit depending on your GPU hardware."); const QString anisotropic_filter = tr("Higher values increase sharpness of textures on sloped surfaces at the cost of GPU resources.\nModern GPUs can handle this setting just fine, even at 16x.\nKeep this on Automatic if you want to use the original setting used by a real PS3."); const QString resolution_scale = tr("Scales the game's resolution by the given percentage.\nThe base resolution is always 1280x720.\nSet this value to 100% if you want to use the normal Resolution options.\nValues below 100% will usually not improve performance."); const QString minimum_scalable_dimension = tr("Only framebuffers greater than this size will be upscaled.\nIncreasing this value might fix problems with missing graphics when upscaling, especially when Write Color Buffers is enabled.\nIf unsure, don't change this option."); const QString dump_color = tr("Enable this option if you get missing graphics or broken lighting ingame.\nMight degrade performance and introduce stuttering in some cases.\nRequired for Demon's Souls."); - const QString vsync = tr("By having this off you might obtain a higher frame rate at the cost of tearing artifacts in the game."); + const QString vsync = tr("By having this off you might obtain a higher framerate at the cost of tearing artifacts in the game."); const QString strict_rendering_mode = tr("Enforces strict compliance to the API specification.\nMight result in degraded performance in some games.\nCan resolve rare cases of missing graphics and flickering.\nIf unsure, don't use this option."); const QString stretch_to_display_area = tr("Overrides the aspect ratio and stretches the image to the full display area."); const QString multithreaded_rsx = tr("Offloads some RSX operations to a secondary thread.\nImproves performance for high-core processors.\nMay cause slowdown in weaker CPUs due to the extra worker thread load."); @@ -218,7 +218,7 @@ public: const QString music_handler = tr("Currently only used for cellMusic emulation.\nSelect Qt to use the default output device of your operating system.\nThis may not be able to play all audio formats."); const QString camera = tr("Select Qt Camera to use the default camera device of your operating system."); const QString camera_type = tr("Depending on the game, you may need to select a specific camera type."); - const QString camera_flip = tr("Flips the camera image either horizontally, vertically, or on both axis."); + const QString camera_flip = tr("Flips the camera image either horizontally, vertically, or on both axes."); const QString camera_id = tr("Select the camera that you want to use during gameplay."); const QString move = tr("PlayStation Move support.\nFake: Experimental! This maps Move controls to DS3 controller mappings.\nMouse: Emulate PSMove with Mouse handler."); const QString buzz = tr("Buzz! support.\nSelect 1 or 2 controllers if the game requires Buzz! controllers and you don't have real controllers.\nSelect Null if the game has support for DualShock or if you have real Buzz! controllers."); @@ -226,7 +226,7 @@ public: const QString ghltar = tr("Guitar Hero Live (GHL) Guitar controller support.\nSelect 1 or 2 controllers if the game requires GHL Guitar controllers and you don't have real guitar controllers.\nSelect Null if the game has support for DualShock or if you have real guitar controllers.\nA real guitar controller can be used at the same time as an emulated guitar controller."); const QString background_input = tr("Allows pad and keyboard input while the game window is unfocused."); const QString show_move_cursor = tr("Shows the raw position of the PS Move input.\nThis can be very helpful during calibration screens."); - const QString midi_devices = tr("Select up to 3 emulated midi devices and their type."); + const QString midi_devices = tr("Select up to 3 emulated MIDI devices and their types."); const QString lock_overlay_input_to_player_one = tr("Locks the native overlay input to the first player."); @@ -247,7 +247,7 @@ public: const QString enter_button_assignment = tr("The button used for enter/accept/confirm in system dialogs.\nChange this to use the Circle button instead, which is the default configuration on Japanese systems and in many Japanese games.\nIn these cases having the cross button assigned can often lead to confusion."); const QString enable_host_root = tr("Required for some Homebrew.\nIf unsure, don't use this option."); const QString limit_cache_size = tr("Automatically removes older files from disk cache on boot if it grows larger than the specified value.\nGames can use the cache folder to temporarily store data outside of system memory. It is not used for long-term storage.\n\nThis setting is only available in the global configuration."); - const QString console_time_offset = tr("Sets the time to be used within the console. This will be applied as an offset that tracks wall clock time.\nCan be reset to current wallclock time by clicking \"Set to Now\"."); + const QString console_time_offset = tr("Sets the time to be used within the console. This will be applied as an offset that tracks wall clock time.\nCan be reset to current wall clock time by clicking \"Set to Now\"."); } settings; const struct gamepad_settings @@ -265,7 +265,7 @@ public: const QString dualsense_linux = tr("The DualSense handler is recommended for official DualSense controllers."); const QString dualsense_other = tr("The DualSense handler is recommended for official DualSense controllers."); const QString xinput = tr("The XInput handler will work with Xbox controllers and many third-party PC-compatible controllers. Pressure sensitive buttons from SCP are supported when SCP's XInput1_3.dll is placed in the main RPCS3 directory. For more details, see the RPCS3 Wiki.").arg(gui::utils::get_link_style()); - const QString evdev = tr("The evdev handler should work with any controller that has linux support.
If your joystick is not being centered properly, read the RPCS3 Wiki for instructions.").arg(gui::utils::get_link_style()); + const QString evdev = tr("The evdev handler should work with any controller that has Linux support.
If your joystick is not being centered properly, read the RPCS3 Wiki for instructions.").arg(gui::utils::get_link_style()); const QString mmjoy = tr("The MMJoystick handler should work with almost any controller recognized by Windows. However, it is recommended that you use the more specific handlers if you have a controller that supports them."); const QString sdl = tr("The SDL handler supports a variety of controllers across different platforms."); @@ -281,7 +281,7 @@ public: const QString mouse_deadzones = tr("The mouse deadzones represent the games' own deadzones on the x and y axes. Games usually enforce their own deadzones to filter out small unwanted stick movements. In consequence, mouse input feels unintuitive since it relies on immediate responsiveness. You can change these values temporarily during gameplay in order to find out the optimal values for your game (Alt+T and Alt+Y for x, Alt+U and Alt+I for y)."); const QString mouse_acceleration = tr("The mouse acceleration can be used to amplify your mouse movements on the x and y axes. Increase these values if your mouse movements feel too slow while playing a game. You can change these values temporarily during gameplay in order to find out the optimal values (Alt+G and Alt+H for x, Alt+J and Alt+K for y). Keep in mind that modern mice usually provide different modes and settings that can be used to change mouse movement speeds as well."); const QString mouse_movement = tr("The mouse movement mode determines how the mouse movement is translated to pad input.
Use the relative mode for traditional mouse movement.
Use the absolute mode to use the mouse's distance to the center of the screen as input value."); - const QString button_assignment = tr("Left-click: remap this button.
Shift + Left-click: add an addition button mapping.
Right-click: clear this button mapping."); + const QString button_assignment = tr("Left-click: remap this button.
Shift + Left-click: add an additional button mapping.
Right-click: clear this button mapping."); } gamepad_settings; }; From fd6829f7576da07e3bb90de8821834d3ce44610c Mon Sep 17 00:00:00 2001 From: Whatcookie Date: Sat, 29 Jul 2023 02:01:01 -0400 Subject: [PATCH 004/184] SPU LLVM: AVX-512 optimization for CFLTU (#14384) - Takes advantage of vrangeps and the new float to uint instructions from AVX-512 - Down from 6 to 3 instructions TODO: Somehow ensure that this is what llvm outputs using CreateFPToUI? --- rpcs3/Emu/Cell/SPURecompiler.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index 1dd9acab29..5c88bba127 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -9871,6 +9871,15 @@ public: a = eval(a * s); value_t r; + + if (m_use_avx512) + { + const auto sc = clamp_smax(a); + r.value = m_ir->CreateFPToUI(sc.value, get_type()); + set_vr(op.rt, r); + return; + } + r.value = m_ir->CreateFPToUI(a.value, get_type()); set_vr(op.rt, select(bitcast(a) > splat(((32 + 127) << 23) - 1), splat(-1), r & ~(bitcast(a) >> 31))); } From c108b319543a3a90d25970077d03903040176c5f Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 31 Jul 2023 01:52:26 +0200 Subject: [PATCH 005/184] Qt: fix data type of find_dialog text edits --- rpcs3/rpcs3qt/find_dialog.cpp | 2 +- rpcs3/rpcs3qt/find_dialog.h | 6 +++--- rpcs3/rpcs3qt/log_frame.cpp | 2 +- rpcs3/rpcs3qt/log_viewer.cpp | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/rpcs3/rpcs3qt/find_dialog.cpp b/rpcs3/rpcs3qt/find_dialog.cpp index 69b7a2f1f4..9c5e805050 100644 --- a/rpcs3/rpcs3qt/find_dialog.cpp +++ b/rpcs3/rpcs3qt/find_dialog.cpp @@ -2,7 +2,7 @@ #include -find_dialog::find_dialog(QTextEdit* edit, QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f), m_text_edit(edit) +find_dialog::find_dialog(QPlainTextEdit* edit, QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f), m_text_edit(edit) { setWindowTitle(tr("Find string")); diff --git a/rpcs3/rpcs3qt/find_dialog.h b/rpcs3/rpcs3qt/find_dialog.h index d99363b654..27baf03c75 100644 --- a/rpcs3/rpcs3qt/find_dialog.h +++ b/rpcs3/rpcs3qt/find_dialog.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include #include #include @@ -11,14 +11,14 @@ class find_dialog : public QDialog Q_OBJECT public: - find_dialog(QTextEdit* edit, QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags()); + find_dialog(QPlainTextEdit* edit, QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags()); private: int m_count_lines = 0; int m_count_total = 0; QLabel* m_label_count_lines; QLabel* m_label_count_total; - QTextEdit* m_text_edit; + QPlainTextEdit* m_text_edit; QLineEdit* m_find_bar; QPushButton* m_find_first; QPushButton* m_find_last; diff --git a/rpcs3/rpcs3qt/log_frame.cpp b/rpcs3/rpcs3qt/log_frame.cpp index d32de0f26c..663878efed 100644 --- a/rpcs3/rpcs3qt/log_frame.cpp +++ b/rpcs3/rpcs3qt/log_frame.cpp @@ -897,7 +897,7 @@ bool log_frame::eventFilter(QObject* object, QEvent* event) if (m_find_dialog && m_find_dialog->isVisible()) m_find_dialog->close(); - m_find_dialog.reset(new find_dialog(static_cast(object), this)); + m_find_dialog.reset(new find_dialog(static_cast(object), this)); } } diff --git a/rpcs3/rpcs3qt/log_viewer.cpp b/rpcs3/rpcs3qt/log_viewer.cpp index dea9da3533..47c78e40b2 100644 --- a/rpcs3/rpcs3qt/log_viewer.cpp +++ b/rpcs3/rpcs3qt/log_viewer.cpp @@ -455,7 +455,7 @@ bool log_viewer::eventFilter(QObject* object, QEvent* event) if (m_find_dialog && m_find_dialog->isVisible()) m_find_dialog->close(); - m_find_dialog.reset(new find_dialog(static_cast(object), this)); + m_find_dialog.reset(new find_dialog(static_cast(object), this)); } } From 8a4617d3c2ffce27abeebcbd14894f8967916379 Mon Sep 17 00:00:00 2001 From: Elad Ashkenazi Date: Mon, 31 Jul 2023 05:00:46 +0300 Subject: [PATCH 006/184] Fix color tag of logs/tty --- bin/GuiConfigs/Classic (Bright).qss | 4 ++-- bin/GuiConfigs/Darker Style by TheMitoSan.qss | 4 ++-- bin/GuiConfigs/Envy.qss | 4 ++-- bin/GuiConfigs/Kuroi (Dark) by Ani.qss | 4 ++-- bin/GuiConfigs/ModernBlue Theme by TheMitoSan.qss | 4 ++-- bin/GuiConfigs/Skyline (Nightfall).qss | 2 +- bin/GuiConfigs/Skyline.qss | 2 +- rpcs3/rpcs3qt/stylesheets.h | 4 ++-- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/bin/GuiConfigs/Classic (Bright).qss b/bin/GuiConfigs/Classic (Bright).qss index 72c7ea7041..9c4c9d441e 100644 --- a/bin/GuiConfigs/Classic (Bright).qss +++ b/bin/GuiConfigs/Classic (Bright).qss @@ -44,13 +44,13 @@ QLabel#gamelist_icon_background_color { } /* log stylesheet */ -QTextEdit#tty_frame { +QPlainTextEdit#tty_frame { background-color:#ffffff; } QLabel#tty_text { color:#000000; } -QTextEdit#log_frame { +QPlainTextEdit#log_frame { background-color:#ffffff; } QLabel#log_level_always { diff --git a/bin/GuiConfigs/Darker Style by TheMitoSan.qss b/bin/GuiConfigs/Darker Style by TheMitoSan.qss index 15be0a5fd3..14a4c1e327 100644 --- a/bin/GuiConfigs/Darker Style by TheMitoSan.qss +++ b/bin/GuiConfigs/Darker Style by TheMitoSan.qss @@ -253,7 +253,7 @@ QLabel#thumbnail_icon_color { } /* Set Log colors */ -QTextEdit#log_frame { +QPlainTextEdit#log_frame { background-color: #000; /* Black */ } QLabel#log_level_always { @@ -285,7 +285,7 @@ QLabel#log_stack { } /* Set TTY colors */ -QTextEdit#tty_frame { +QPlainTextEdit#tty_frame { background-color: #000; /* Black */ } QLabel#tty_text { diff --git a/bin/GuiConfigs/Envy.qss b/bin/GuiConfigs/Envy.qss index fbdda99643..a78ba04f58 100644 --- a/bin/GuiConfigs/Envy.qss +++ b/bin/GuiConfigs/Envy.qss @@ -579,7 +579,7 @@ QLabel#thumbnail_icon_color { } /* Log colors */ -QTextEdit#log_frame { +QPlainTextEdit#log_frame { background-color: #23262d; } @@ -620,7 +620,7 @@ QLabel#log_stack { } /* TTY colors */ -QTextEdit#tty_frame { +QPlainTextEdit#tty_frame { background-color: #23262d; } diff --git a/bin/GuiConfigs/Kuroi (Dark) by Ani.qss b/bin/GuiConfigs/Kuroi (Dark) by Ani.qss index 7e7f5d4b84..54c667213b 100644 --- a/bin/GuiConfigs/Kuroi (Dark) by Ani.qss +++ b/bin/GuiConfigs/Kuroi (Dark) by Ani.qss @@ -292,7 +292,7 @@ QLabel#debugger_frame_pc { } /* Set Log colors */ -QTextEdit#log_frame { +QPlainTextEdit#log_frame { background-color: #000000; /* Black */ } QLabel#log_level_always { @@ -323,7 +323,7 @@ QLabel#log_stack { color: #ffffff; /* White */ } /* Set TTY colors */ -QTextEdit#tty_frame { +QPlainTextEdit#tty_frame { background-color: #000000; /* Black */ } QLabel#tty_text { diff --git a/bin/GuiConfigs/ModernBlue Theme by TheMitoSan.qss b/bin/GuiConfigs/ModernBlue Theme by TheMitoSan.qss index f8d41c3af0..410db682f7 100644 --- a/bin/GuiConfigs/ModernBlue Theme by TheMitoSan.qss +++ b/bin/GuiConfigs/ModernBlue Theme by TheMitoSan.qss @@ -250,7 +250,7 @@ QLabel#thumbnail_icon_color { } /* Set Log colors */ -QTextEdit#log_frame { +QPlainTextEdit#log_frame { background-color: #181d24; /* Black */ } QLabel#log_level_always { @@ -282,7 +282,7 @@ QLabel#log_stack { } /* Set TTY colors */ -QTextEdit#tty_frame { +QPlainTextEdit#tty_frame { background-color: #181d24; /* Black */ } QLabel#tty_text { diff --git a/bin/GuiConfigs/Skyline (Nightfall).qss b/bin/GuiConfigs/Skyline (Nightfall).qss index fbd6b25194..625a6a28b6 100644 --- a/bin/GuiConfigs/Skyline (Nightfall).qss +++ b/bin/GuiConfigs/Skyline (Nightfall).qss @@ -611,7 +611,7 @@ QLineEdit:focus { } /* Log colors */ -QTextEdit#log_frame { +QPlainTextEdit#log_frame { background-color: #111525; } diff --git a/bin/GuiConfigs/Skyline.qss b/bin/GuiConfigs/Skyline.qss index c1339e96ea..ef3c7c6857 100644 --- a/bin/GuiConfigs/Skyline.qss +++ b/bin/GuiConfigs/Skyline.qss @@ -619,7 +619,7 @@ QLineEdit:focus { } /* Log colors */ -QTextEdit#log_frame { +QPlainTextEdit#log_frame { background-color: #FFFFFF; } diff --git a/rpcs3/rpcs3qt/stylesheets.h b/rpcs3/rpcs3qt/stylesheets.h index 15c32b7473..b3300cb98e 100644 --- a/rpcs3/rpcs3qt/stylesheets.h +++ b/rpcs3/rpcs3qt/stylesheets.h @@ -65,11 +65,11 @@ namespace gui "QDockWidget::close-button, QDockWidget::float-button{ background-color: #e3e3e3; }" // log frame tty - "QTextEdit#tty_frame { background-color: #ffffff; }" + "QPlainTextEdit#tty_frame { background-color: #ffffff; }" "QLabel#tty_text { color: #000000; }" // log frame log - "QTextEdit#log_frame { background-color: #ffffff; }" + "QPlainTextEdit#log_frame { background-color: #ffffff; }" "QLabel#log_level_always { color: #107896; }" "QLabel#log_level_fatal { color: #ff00ff; }" "QLabel#log_level_error { color: #C02F1D; }" From 213b8102797f6663b02f0af19bf40f974c55ad99 Mon Sep 17 00:00:00 2001 From: Elad Ashkenazi Date: Mon, 31 Jul 2023 05:20:18 +0300 Subject: [PATCH 007/184] Debugger: Transition to plain text edit --- rpcs3/rpcs3qt/debugger_frame.cpp | 12 ++++++------ rpcs3/rpcs3qt/debugger_frame.h | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/rpcs3/rpcs3qt/debugger_frame.cpp b/rpcs3/rpcs3qt/debugger_frame.cpp index d8131562c5..9a34a09959 100644 --- a/rpcs3/rpcs3qt/debugger_frame.cpp +++ b/rpcs3/rpcs3qt/debugger_frame.cpp @@ -122,13 +122,13 @@ debugger_frame::debugger_frame(std::shared_ptr gui_settings, QWidg hbox_b_main->addStretch(); // Misc state - m_misc_state = new QTextEdit(this); - m_misc_state->setLineWrapMode(QTextEdit::NoWrap); + m_misc_state = new QPlainTextEdit(this); + m_misc_state->setLineWrapMode(QPlainTextEdit::NoWrap); m_misc_state->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); // Registers - m_regs = new QTextEdit(this); - m_regs->setLineWrapMode(QTextEdit::NoWrap); + m_regs = new QPlainTextEdit(this); + m_regs->setLineWrapMode(QPlainTextEdit::NoWrap); m_regs->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); m_debugger_list->setFont(m_mono); @@ -1147,7 +1147,7 @@ void debugger_frame::WritePanels() int loc = m_misc_state->verticalScrollBar()->value(); int hloc = m_misc_state->horizontalScrollBar()->value(); m_misc_state->clear(); - m_misc_state->setText(qstr(cpu->dump_misc())); + m_misc_state->setPlainText(qstr(cpu->dump_misc())); m_misc_state->verticalScrollBar()->setValue(loc); m_misc_state->horizontalScrollBar()->setValue(hloc); @@ -1156,7 +1156,7 @@ void debugger_frame::WritePanels() m_regs->clear(); m_last_reg_state.clear(); cpu->dump_regs(m_last_reg_state, m_dump_reg_func_data); - m_regs->setText(qstr(m_last_reg_state)); + m_regs->setPlainText(qstr(m_last_reg_state)); m_regs->verticalScrollBar()->setValue(loc); m_regs->horizontalScrollBar()->setValue(hloc); diff --git a/rpcs3/rpcs3qt/debugger_frame.h b/rpcs3/rpcs3qt/debugger_frame.h index 4ccc62a1f3..a0381f9898 100644 --- a/rpcs3/rpcs3qt/debugger_frame.h +++ b/rpcs3/rpcs3qt/debugger_frame.h @@ -5,7 +5,7 @@ #include "custom_dock_widget.h" #include -#include +#include #include #include @@ -42,8 +42,8 @@ class debugger_frame : public custom_dock_widget debugger_list* m_debugger_list; QSplitter* m_right_splitter; QFont m_mono; - QTextEdit* m_misc_state; - QTextEdit* m_regs; + QPlainTextEdit* m_misc_state; + QPlainTextEdit* m_regs; QPushButton* m_go_to_addr; QPushButton* m_go_to_pc; QPushButton* m_btn_step; From 53c1da8f94c70685fd0f50a5b1052a83140e79e1 Mon Sep 17 00:00:00 2001 From: Elad Ashkenazi Date: Mon, 31 Jul 2023 05:21:17 +0300 Subject: [PATCH 008/184] Qt: Fix elf_memory_dumping_dialog PPU address --- rpcs3/rpcs3qt/elf_memory_dumping_dialog.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rpcs3/rpcs3qt/elf_memory_dumping_dialog.cpp b/rpcs3/rpcs3qt/elf_memory_dumping_dialog.cpp index 0eb7f41844..b96699197a 100644 --- a/rpcs3/rpcs3qt/elf_memory_dumping_dialog.cpp +++ b/rpcs3/rpcs3qt/elf_memory_dumping_dialog.cpp @@ -49,7 +49,7 @@ elf_memory_dumping_dialog::elf_memory_dumping_dialog(u32 ppu_debugger_addr, std: m_ls_address_input = make_hex_edit(5); m_segment_flags_input = make_hex_edit(1); m_segment_flags_input->setText("0x7"); // READ WRITE EXEC - m_ppu_address_input->setText(QStringLiteral("0x%x").arg(ppu_debugger_addr & -0x10000, 2, 16)); // SPU code segments are usually 128 bytes aligned, let's make it even 64k so the user would have to type himself the lower part to avoid human errors. + m_ppu_address_input->setText(QStringLiteral("0x%1").arg(ppu_debugger_addr & -0x10000, 1, 16)); // SPU code segments are usually 128 bytes aligned, let's make it even 64k so the user would have to type himself the lower part to avoid human errors. QPushButton* add_segment_button = new QPushButton(QStringLiteral("+")); add_segment_button->setToolTip(tr("Add new segment")); @@ -170,7 +170,7 @@ void elf_memory_dumping_dialog::add_new_segment() } } - auto item = new QListWidgetItem(tr("PPU Address: 0x%0, LS Address: 0x%1, Segment Size: 0x%2, Flags: 0x%3").arg(+vm::try_get_addr(data.src_addr).first, 2, 16).arg(data.ls_addr, 2, 16).arg(data.segment_size, 2, 16).arg(data.flags, 2, 16), m_seg_list); + auto item = new QListWidgetItem(tr("PPU Address: 0x%0, LS Address: 0x%1, Segment Size: 0x%2, Flags: 0x%3").arg(+vm::try_get_addr(data.src_addr).first, 5, 16).arg(data.ls_addr, 2, 16).arg(data.segment_size, 2, 16).arg(data.flags, 2, 16), m_seg_list); item->setData(Qt::UserRole, QVariant::fromValue(data)); m_seg_list->setCurrentItem(item); } From b12edf70bbd8e07c2dc93e0baf5c0fc5f96b386a Mon Sep 17 00:00:00 2001 From: Eladash Date: Mon, 31 Jul 2023 10:24:16 +0300 Subject: [PATCH 009/184] Optimize RSX Debugger --- rpcs3/Emu/RSX/RSXDisAsm.cpp | 6 +- rpcs3/Emu/RSX/RSXThread.cpp | 4 +- rpcs3/Emu/RSX/gcm_enums.cpp | 21 +- rpcs3/Emu/RSX/gcm_printing.cpp | 36 +- rpcs3/Emu/RSX/gcm_printing.h | 4 +- rpcs3/Emu/RSX/rsx_decode.h | 1134 ++++++++++++++++---------------- rpcs3/rpcs3qt/rsx_debugger.cpp | 9 +- 7 files changed, 622 insertions(+), 592 deletions(-) diff --git a/rpcs3/Emu/RSX/RSXDisAsm.cpp b/rpcs3/Emu/RSX/RSXDisAsm.cpp index 46ef4e4123..5d25284c61 100644 --- a/rpcs3/Emu/RSX/RSXDisAsm.cpp +++ b/rpcs3/Emu/RSX/RSXDisAsm.cpp @@ -109,6 +109,8 @@ u32 RSXDisAsm::disasm(u32 pc) pc += 4; + std::string str; + for (u32 i = 0; i < count; i++, pc += 4) { if (!try_read_op(pc)) @@ -137,7 +139,9 @@ u32 RSXDisAsm::disasm(u32 pc) continue; } - std::string str = rsx::get_pretty_printing_function(id)(id, m_op); + str.clear(); + rsx::get_pretty_printing_function(id)(str, id, m_op); + Write(str, m_mode == cpu_disasm_mode::list ? i : count, non_inc, id); } diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index 6f82e38a39..1e9c907f40 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -3198,7 +3198,9 @@ namespace rsx } } - fmt::append(result, "[%04x] %s\n", i, ensure(rsx::get_pretty_printing_function(i))(i, method_registers.registers[i])); + fmt::append(result, "[%04x] ", i); + ensure(rsx::get_pretty_printing_function(i))(result, i, method_registers.registers[i]); + result += '\n'; } } diff --git a/rpcs3/Emu/RSX/gcm_enums.cpp b/rpcs3/Emu/RSX/gcm_enums.cpp index af0a8ab93b..b6a0292fd9 100644 --- a/rpcs3/Emu/RSX/gcm_enums.cpp +++ b/rpcs3/Emu/RSX/gcm_enums.cpp @@ -612,8 +612,21 @@ void fmt_class_string::format(std::string& out, u64 arg) namespace rsx { - std::string print_boolean(bool b) + enum class boolean_to_string_t : u8; +} + +template <> +void fmt_class_string::format(std::string& out, u64 arg) +{ + format_enum(out, arg, [](boolean_to_string_t value) { - return b ? "enabled" : "disabled"; - } -} // end namespace rsx + switch (value) + { + case boolean_to_string_t{+true}: return "true"; + case boolean_to_string_t{+false}: return "false"; + default: break; // TODO: This is technically unreachable but need needs to be reachable when value is not 1 or 0 + } + + return unknown; + }); +} diff --git a/rpcs3/Emu/RSX/gcm_printing.cpp b/rpcs3/Emu/RSX/gcm_printing.cpp index b0df9a18cb..d7e5e6c811 100644 --- a/rpcs3/Emu/RSX/gcm_printing.cpp +++ b/rpcs3/Emu/RSX/gcm_printing.cpp @@ -902,18 +902,20 @@ namespace #undef KEY_STR } -std::string rsx::get_method_name(const u32 id) +std::pair rsx::get_method_name(u32 id, std::string& string_name) { const auto found = methods_name.find(id); if (found != methods_name.end()) { - std::string prefix("CELL_GCM_"sv); - prefix.append(found->second.data(), found->second.size()); - return prefix; + constexpr std::string_view prefix = "CELL_GCM_"; + + return {prefix, found->second}; } - return fmt::format("Unnamed method 0x%04x", id); + string_name.clear(); + fmt::append(string_name, "Unnamed method 0x%04x", id); + return {}; } // Various parameter pretty printing function @@ -984,15 +986,15 @@ namespace namespace { template - std::string register_pretty_function(u32 /*id*/, u32 arg) + void register_pretty_function(std::string& out, u32 /*id*/, u32 arg) { - return rsx::registers_decoder::dump(arg); + rsx::registers_decoder::dump(out, arg); } template - std::array create_printing_table(std::integer_sequence) + std::array create_printing_table(std::integer_sequence) { - std::array result{}; + std::array result{}; ((result[opcode_list[Index * 5 + 0]] = ®ister_pretty_function, result[opcode_list[Index * 5 + 1]] = ®ister_pretty_function, @@ -1022,7 +1024,7 @@ namespace };*/ } -std::add_pointer_t rsx::get_pretty_printing_function(u32 id) +std::add_pointer_t rsx::get_pretty_printing_function(u32 id) { const auto found = id < printing_functions.size() ? printing_functions[id] : nullptr; @@ -1031,11 +1033,17 @@ std::add_pointer_t rsx::get_pretty_printing_function(u32 return found; } - return [](u32 id, u32 v) + return [](std::string& result, u32 id, u32 v) { - const std::string name = rsx::get_method_name(id); - const std::string_view view = name, prefix = "CELL_GCM_"sv; + std::string string_name; + const auto [name_prefix, name] = rsx::get_method_name(id, string_name); - return fmt::format("%s: 0x%08x", name.starts_with("CELL_GCM_"sv) ? view.substr(prefix.size()) : view, v); + if (!string_name.empty()) + { + fmt::append(result, "%s: 0x%08x", string_name, v); + return; + } + + fmt::append(result, "%s: 0x%08x", name, v); }; } diff --git a/rpcs3/Emu/RSX/gcm_printing.h b/rpcs3/Emu/RSX/gcm_printing.h index eb803f93b6..46c1b7ea1a 100644 --- a/rpcs3/Emu/RSX/gcm_printing.h +++ b/rpcs3/Emu/RSX/gcm_printing.h @@ -6,7 +6,7 @@ namespace rsx { - std::string get_method_name(u32 id); + std::pair get_method_name(u32 id, std::string& result_str); - std::add_pointer_t get_pretty_printing_function(u32 id); + std::add_pointer_t get_pretty_printing_function(u32 id); } diff --git a/rpcs3/Emu/RSX/rsx_decode.h b/rpcs3/Emu/RSX/rsx_decode.h index edbef500f6..439937309f 100644 --- a/rpcs3/Emu/RSX/rsx_decode.h +++ b/rpcs3/Emu/RSX/rsx_decode.h @@ -10,7 +10,12 @@ namespace rsx { - std::string print_boolean(bool b); + enum class boolean_to_string_t : u8 {}; + + constexpr boolean_to_string_t print_boolean(bool b) + { + return boolean_to_string_t{static_cast(b)}; + } template struct registers_decoder @@ -30,12 +35,12 @@ struct registers_decoder { const u32 value; - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV406E Ref: 0x%08x", decoded.value); + fmt::append(out, "NV406E Ref: 0x%08x", decoded.value); } }; @@ -48,7 +53,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 origin_x() const { @@ -61,9 +66,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Viewport: x: %u width: %u", decoded.origin_x(), decoded.width()); + fmt::append(out, "Viewport: x: %u width: %u", decoded.origin_x(), decoded.width()); } }; @@ -76,7 +81,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 origin_y() const { @@ -89,9 +94,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Viewport: y: %u height: %u", decoded.origin_y(), decoded.height()); + fmt::append(out, "Viewport: y: %u height: %u", decoded.origin_y(), decoded.height()); } }; @@ -104,7 +109,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 origin_x() const { @@ -117,9 +122,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Scissor: x = " + std::to_string(decoded.origin_x()) + " width = " + std::to_string(decoded.width()); + fmt::append(out, "Scissor: x: %u width: %u", decoded.origin_x(), decoded.width()); } }; @@ -132,7 +137,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 origin_y() const { @@ -145,9 +150,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Scissor: y = " + std::to_string(decoded.origin_y()) + " height = " + std::to_string(decoded.height()); + fmt::append(out, "Scissor: y: %u height: %u", decoded.origin_y(), decoded.height()); } }; @@ -160,7 +165,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 origin_x() const { @@ -173,9 +178,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Surface: clip x = " + std::to_string(decoded.origin_x()) + " width = " + std::to_string(decoded.width()); + fmt::append(out, "Surface: clip x: %u width: %u", decoded.origin_x(), decoded.width()); } }; @@ -188,7 +193,7 @@ struct registers_decoder< NV4097_SET_SURFACE_CLIP_VERTICAL> u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 origin_y() const { @@ -201,9 +206,9 @@ struct registers_decoder< NV4097_SET_SURFACE_CLIP_VERTICAL> } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Surface: clip y = " + std::to_string(decoded.origin_y()) + " height = " + std::to_string(decoded.height()); + fmt::append(out, "Surface: clip y: %u height: %u", decoded.origin_y(), decoded.height()); } }; @@ -216,7 +221,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 origin_x() const { @@ -229,9 +234,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Clear: rect x = " + std::to_string(decoded.origin_x()) + " width = " + std::to_string(decoded.width()); + fmt::append(out, "Clear: rect x: %u width: %u", decoded.origin_x(), decoded.width()); } }; @@ -244,7 +249,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 origin_y() const { @@ -257,9 +262,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Clear: rect y = " + std::to_string(decoded.origin_y()) + " height = " + std::to_string(decoded.height()); + fmt::append(out, "Clear: rect y: %u height: %u", decoded.origin_y(), decoded.height()); } }; @@ -272,7 +277,7 @@ struct registers_decoder< NV3089_CLIP_POINT> u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 clip_x() const { @@ -285,9 +290,9 @@ struct registers_decoder< NV3089_CLIP_POINT> } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Blit engine: clip x = " + std::to_string(decoded.clip_x()) + " y = " + std::to_string(decoded.clip_y()); + fmt::append(out, "Blit engine: clip x: %u y: %u", decoded.clip_x(), decoded.clip_y()); } }; @@ -300,7 +305,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 clip_width() const { @@ -313,9 +318,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Blit engine: clip width = " + std::to_string(decoded.clip_width()) + " height = " + std::to_string(decoded.clip_height()); + fmt::append(out, "Blit engine: clip width: %u height: %u", decoded.clip_width(), decoded.clip_height()); } }; @@ -328,7 +333,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 x() const { @@ -341,9 +346,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Blit engine: output x = " + std::to_string(decoded.x()) + " y = " + std::to_string(decoded.y()); + fmt::append(out, "Blit engine: output x: %u y: %u", decoded.x(), decoded.y()); } }; @@ -356,7 +361,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 window_offset_x() const { @@ -369,9 +374,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Window: offset x: %u y: %u", decoded.window_offset_x(), decoded.window_offset_y()); + fmt::append(out, "Window: offset x: %u y: %u", decoded.window_offset_x(), decoded.window_offset_y()); } }; @@ -385,7 +390,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 width() const { @@ -398,9 +403,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Blit engine: output width = " + std::to_string(decoded.width()) + " height = " + std::to_string(decoded.height()); + fmt::append(out, "Blit engine: output width: %u height: %u", decoded.width(), decoded.height()); } }; @@ -413,7 +418,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 width() const { @@ -426,9 +431,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Blit engine: input width = " + std::to_string(decoded.width()) + " height = " + std::to_string(decoded.height()); + fmt::append(out, "Blit engine: input width: %u height: %u", decoded.width(), decoded.height()); } }; @@ -441,7 +446,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 alignment() const { @@ -454,9 +459,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Blit engine: output alignment = " + std::to_string(decoded.alignment()) + " pitch = " + std::to_string(decoded.pitch()); + fmt::append(out, "Blit engine: output alignment: %u pitch: %u", decoded.alignment(), decoded.pitch()); } }; @@ -469,7 +474,7 @@ struct registers_decoder< NV308A_POINT> u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 x() const { @@ -482,9 +487,9 @@ struct registers_decoder< NV308A_POINT> } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "NV308A: x = " + std::to_string(decoded.x()) + " y = " + std::to_string(decoded.y()); + fmt::append(out, "NV308A: x: %u y: %u", decoded.x(), decoded.y()); } }; @@ -497,7 +502,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 mask() const { @@ -505,10 +510,10 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - std::string result = "Transform program enabled inputs:"; - const std::string input_names[] = + out += "Transform program enabled inputs:"; + constexpr std::string_view input_names[] = { "in_pos", "in_weight", "in_normal", "in_diff_color", "in_spec_color", @@ -517,10 +522,15 @@ struct registers_decoder "in_tc0", "in_tc1", "in_tc2", "in_tc3", "in_tc4", "in_tc5", "in_tc6", "in_tc7" }; - for (unsigned i = 0; i < 16; i++) + + for (u32 i = 0; i < 16; i++) + { if (decoded.mask() & (1 << i)) - result += input_names[i] + " "; - return result; + { + out += ' '; + out += input_names[i]; + } + } } }; @@ -533,7 +543,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 frequency_divider_operation_mask() const { @@ -541,13 +551,26 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - std::string result = "Frequency divider: "; - for (unsigned i = 0; i < 16; i++) - if (decoded.frequency_divider_operation_mask() & (1 << i)) - result += std::to_string(i) + " "; - return result; + out += "Frequency divider:"; + + const u32 mask = decoded.frequency_divider_operation_mask(); + + if (!mask) + { + out += " (none)"; + return; + } + + for (u32 i = 0; i < 16; i++) + { + if (mask & (1 << i)) + { + out += ' '; + fmt::append(out, "%u", i); + } + } } }; @@ -568,9 +591,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Depth: test " + print_boolean(decoded.depth_test_enabled()); + fmt::append(out, "Depth: test %s", print_boolean(decoded.depth_test_enabled())); } }; @@ -591,9 +614,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Depth: write " + print_boolean(decoded.depth_write_enabled()); + fmt::append(out, "Depth: write: %s", print_boolean(decoded.depth_write_enabled())); } }; @@ -606,7 +629,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool depth_clip_enabled() const { @@ -624,11 +647,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Depth: clip_enabled " + print_boolean(decoded.depth_clip_enabled()) + - " clamp " + print_boolean(decoded.depth_clamp_enabled()) + - " ignore_w " + print_boolean(decoded.depth_clip_ignore_w()); + fmt::append(out, "Depth: clip_enabled: %s, clamp: %s, ignore_w: %s", print_boolean(decoded.depth_clip_enabled()), print_boolean(decoded.depth_clamp_enabled()) , print_boolean(decoded.depth_clip_ignore_w())); } }; @@ -649,9 +670,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Alpha: test " + print_boolean(decoded.alpha_test_enabled()); + fmt::append(out, "Alpha: test %s", print_boolean(decoded.alpha_test_enabled())); } }; @@ -672,9 +693,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Stencil: test " + print_boolean(decoded.stencil_test_enabled()); + fmt::append(out, "Stencil: test %s", print_boolean(decoded.stencil_test_enabled())); } }; @@ -695,9 +716,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Restart Index: " + print_boolean(decoded.restart_index_enabled()); + fmt::append(out, "Restart Index: %s", print_boolean(decoded.restart_index_enabled())); } }; @@ -718,9 +739,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Depth: bound test " + print_boolean(decoded.depth_bound_enabled()); + fmt::append(out, "Depth: bound test %s", print_boolean(decoded.depth_bound_enabled())); } }; @@ -741,9 +762,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Logic: " + print_boolean(decoded.logic_op_enabled()); + fmt::append(out, "Logic: %s", print_boolean(decoded.logic_op_enabled())); } }; @@ -764,9 +785,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Dither: " + print_boolean(decoded.dither_enabled()); + fmt::append(out, "Dither: %s", print_boolean(decoded.dither_enabled())); } }; @@ -787,9 +808,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Blend: " + print_boolean(decoded.blend_enabled()); + fmt::append(out, "Blend: %s", print_boolean(decoded.blend_enabled())); } }; @@ -810,9 +831,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Line: smooth " + print_boolean(decoded.line_smooth_enabled()); + fmt::append(out, "Line smooth: %s", print_boolean(decoded.line_smooth_enabled())); } }; @@ -833,9 +854,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Polygon: offset point " + print_boolean(decoded.poly_offset_point_enabled()); + fmt::append(out, "Polygon: offset point: %s", print_boolean(decoded.poly_offset_point_enabled())); } }; @@ -856,9 +877,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Polygon: offset line " + print_boolean(decoded.poly_offset_line_enabled()); + fmt::append(out, "Polygon: offset line: %s", print_boolean(decoded.poly_offset_line_enabled())); } }; @@ -879,9 +900,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Polygon: offset fill " + print_boolean(decoded.poly_offset_fill_enabled()); + fmt::append(out, "Polygon: offset fill: %s", print_boolean(decoded.poly_offset_fill_enabled())); } }; @@ -902,9 +923,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Cull face: " + print_boolean(decoded.cull_face_enabled()); + fmt::append(out, "Cull face: %s", print_boolean(decoded.cull_face_enabled())); } }; @@ -925,9 +946,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Polygon: smooth " + print_boolean(decoded.poly_smooth_enabled()); + fmt::append(out, "Polygon: smooth: %s", print_boolean(decoded.poly_smooth_enabled())); } }; @@ -948,9 +969,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Stencil: per side " + print_boolean(decoded.two_sided_stencil_test_enabled()); + fmt::append(out, "Stencil: per side: %s", print_boolean(decoded.two_sided_stencil_test_enabled())); } }; @@ -971,9 +992,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Light: per side " + print_boolean(decoded.two_sided_lighting_enabled()); + fmt::append(out, "Light: per side: %s", print_boolean(decoded.two_sided_lighting_enabled())); } }; @@ -986,7 +1007,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 depth_bound_min() const { @@ -994,9 +1015,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Depth: bound min = " + std::to_string(decoded.depth_bound_min()); + fmt::append(out, "Depth: bound min: %g", decoded.depth_bound_min()); } }; @@ -1009,7 +1030,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 depth_bound_max() const { @@ -1017,9 +1038,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Depth: bound max = " + std::to_string(decoded.depth_bound_max()); + fmt::append(out, "Depth: bound max: %g", decoded.depth_bound_max()); } }; @@ -1032,7 +1053,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 fog_param_0() const { @@ -1040,9 +1061,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Fog: param 0 = " + std::to_string(decoded.fog_param_0()); + fmt::append(out, "Fog: param 0: %g", decoded.fog_param_0()); } }; @@ -1055,7 +1076,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 fog_param_1() const { @@ -1063,9 +1084,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Fog: param 1 = " + std::to_string(decoded.fog_param_1()); + fmt::append(out, "Fog: param 1: %g", decoded.fog_param_1()); } }; @@ -1078,7 +1099,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 clip_min() const { @@ -1086,9 +1107,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Depth: clip min = " + std::to_string(decoded.clip_min()); + fmt::append(out, "Depth: clip min: %g", decoded.clip_min()); } }; @@ -1101,7 +1122,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 clip_max() const { @@ -1109,9 +1130,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Depth: clip max = " + std::to_string(decoded.clip_max()); + fmt::append(out, "Depth: clip max: %g", decoded.clip_max()); } }; @@ -1124,7 +1145,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 polygon_offset_scale_factor() const { @@ -1132,9 +1153,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Polygon: offset scale = " + std::to_string(decoded.polygon_offset_scale_factor()); + fmt::append(out, "Polygon: offset scale: %g", decoded.polygon_offset_scale_factor()); } }; @@ -1147,7 +1168,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 polygon_offset_scale_bias() const { @@ -1155,9 +1176,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Polygon: offset bias = " + std::to_string(decoded.polygon_offset_scale_bias()); + fmt::append(out, "Polygon: offset bias: %g", decoded.polygon_offset_scale_bias()); } }; @@ -1170,7 +1191,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 viewport_scale_x() const { @@ -1178,9 +1199,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Viewport: scale x = " + std::to_string(decoded.viewport_scale_x()); + fmt::append(out, "Viewport: scale x: %g", decoded.viewport_scale_x()); } }; @@ -1193,7 +1214,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 viewport_scale_y() const { @@ -1201,9 +1222,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Viewport: scale y = " + std::to_string(decoded.viewport_scale_y()); + fmt::append(out, "Viewport: scale y: %g", decoded.viewport_scale_y()); } }; @@ -1216,7 +1237,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 viewport_scale_z() const { @@ -1224,9 +1245,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Viewport: scale z = " + std::to_string(decoded.viewport_scale_z()); + fmt::append(out, "Viewport: scale z: %g", decoded.viewport_scale_z()); } }; @@ -1239,7 +1260,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 viewport_scale_w() const { @@ -1247,9 +1268,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Viewport: scale w = " + std::to_string(decoded.viewport_scale_w()); + fmt::append(out, "Viewport: scale w: %g", decoded.viewport_scale_w()); } }; @@ -1262,7 +1283,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 viewport_offset_x() const { @@ -1270,9 +1291,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Viewport: offset x = " + std::to_string(decoded.viewport_offset_x()); + fmt::append(out, "Viewport: offset x: %g", decoded.viewport_offset_x()); } }; @@ -1285,7 +1306,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 viewport_offset_y() const { @@ -1293,9 +1314,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Viewport: offset y = " + std::to_string(decoded.viewport_offset_y()); + fmt::append(out, "Viewport: offset y: %g", decoded.viewport_offset_y()); } }; @@ -1308,7 +1329,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 viewport_offset_z() const { @@ -1316,9 +1337,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Viewport: offset z = " + std::to_string(decoded.viewport_offset_z()); + fmt::append(out, "Viewport: offset z: %g", decoded.viewport_offset_z()); } }; @@ -1331,7 +1352,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 viewport_offset_w() const { @@ -1339,9 +1360,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Viewport: offset w = " + std::to_string(decoded.viewport_offset_w()); + fmt::append(out, "Viewport: offset w: %g", decoded.viewport_offset_w()); } }; @@ -1354,7 +1375,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 restart_index() const { @@ -1362,9 +1383,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Restart index: " + std::to_string(decoded.restart_index()); + fmt::append(out, "Restart index: %u", decoded.restart_index()); } }; @@ -1377,7 +1398,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 surface_a_offset() const { @@ -1385,9 +1406,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Surface: A offset 0x%x", decoded.surface_a_offset()); + fmt::append(out, "Surface: A offset 0x%x", decoded.surface_a_offset()); } }; @@ -1400,7 +1421,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 surface_b_offset() const { @@ -1408,9 +1429,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Surface: B offset 0x%x", decoded.surface_b_offset()); + fmt::append(out, "Surface: B offset 0x%x", decoded.surface_b_offset()); } }; @@ -1423,7 +1444,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 surface_c_offset() const { @@ -1431,9 +1452,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Surface: C offset 0x%x", decoded.surface_c_offset()); + fmt::append(out, "Surface: C offset 0x%x", decoded.surface_c_offset()); } }; @@ -1446,7 +1467,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 surface_d_offset() const { @@ -1454,9 +1475,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Surface: D offset 0x%x", decoded.surface_d_offset()); + fmt::append(out, "Surface: D offset 0x%x", decoded.surface_d_offset()); } }; @@ -1469,7 +1490,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 surface_a_pitch() const { @@ -1477,9 +1498,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Surface: A pitch " + std::to_string(decoded.surface_a_pitch()); + fmt::append(out, "Surface: A pitch: %u", decoded.surface_a_pitch()); } }; @@ -1492,7 +1513,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 surface_b_pitch() const { @@ -1500,9 +1521,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Surface: B pitch " + std::to_string(decoded.surface_b_pitch()); + fmt::append(out, "Surface: B pitch: %u", decoded.surface_b_pitch()); } }; @@ -1515,7 +1536,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 surface_c_pitch() const { @@ -1523,9 +1544,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Surface: C pitch " + std::to_string(decoded.surface_c_pitch()); + fmt::append(out, "Surface: C pitch: %u", decoded.surface_c_pitch()); } }; @@ -1538,7 +1559,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 surface_d_pitch() const { @@ -1546,9 +1567,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Surface: D pitch " + std::to_string(decoded.surface_d_pitch()); + fmt::append(out, "Surface: D pitch: %u", decoded.surface_d_pitch()); } }; @@ -1561,7 +1582,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 surface_z_offset() const { @@ -1569,9 +1590,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Surface: Z offset " + std::to_string(decoded.surface_z_offset()); + fmt::append(out, "Surface: Z offset: %u", decoded.surface_z_offset()); } }; @@ -1584,7 +1605,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 surface_z_pitch() const { @@ -1592,9 +1613,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Surface: Z pitch " + std::to_string(decoded.surface_z_pitch()); + fmt::append(out, "Surface: Z pitch: %u", decoded.surface_z_pitch()); } }; @@ -1607,7 +1628,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 output_mask() const { @@ -1615,9 +1636,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - const std::string output_names[] = + static constexpr std::string_view output_names[] = { "diffuse_color", "specular_color", @@ -1642,11 +1663,19 @@ struct registers_decoder "tc6", "tc7" }; - std::string result = "Transform program outputs:"; - for (unsigned i = 0; i < 22; i++) - if (decoded.output_mask() & (1 << i)) - result += output_names[i] + " "; - return result; + + out += "Transform program outputs:"; + + const u32 mask = decoded.output_mask(); + + for (u32 i = 0; i < 22; i++) + { + if (mask & (1 << i)) + { + out += ' '; + out += output_names[i]; + } + } } }; @@ -1659,7 +1688,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 shader_ctrl() const { @@ -1667,12 +1696,11 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Shader control: raw_value =" + std::to_string(decoded.shader_ctrl()) + - " reg_count = " + std::to_string((decoded.shader_ctrl() >> 24) & 0xFF) + - ((decoded.shader_ctrl() & CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT) ? " depth_replace " : "") + - ((decoded.shader_ctrl() & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS) ? " 32b_exports " : ""); + fmt::append(out, "Shader control: raw_value: 0x%x reg_count: %u%s%s", + decoded.shader_ctrl(), ((decoded.shader_ctrl() >> 24) & 0xFF), ((decoded.shader_ctrl() & CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT) ? " depth_replace" : ""), + ((decoded.shader_ctrl() & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS) ? " 32b_exports" : "")); } }; @@ -1685,7 +1713,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool srgb_output_enabled() const { @@ -1693,9 +1721,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Shader packer control: srgb_enabled = " + std::to_string(decoded.srgb_output_enabled()); + fmt::append(out, "Shader packer control: srgb_enabled: %s", print_boolean(decoded.srgb_output_enabled())); } }; @@ -1708,7 +1736,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 vertex_data_base_offset() const { @@ -1716,9 +1744,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Vertex: base offset 0x%x", decoded.vertex_data_base_offset()); + fmt::append(out, "Vertex: base offset 0x%x", decoded.vertex_data_base_offset()); } }; @@ -1731,7 +1759,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 index_array_offset() const { @@ -1739,9 +1767,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Index: array offset 0x%x", decoded.index_array_offset()); + fmt::append(out, "Index: array offset 0x%x", decoded.index_array_offset()); } }; @@ -1754,7 +1782,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 vertex_data_base_index() const { @@ -1762,9 +1790,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Vertex: base index " + std::to_string(decoded.vertex_data_base_index()); + fmt::append(out, "Vertex: base index: %u", decoded.vertex_data_base_index()); } }; @@ -1777,7 +1805,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 shader_program_address() const { @@ -1785,10 +1813,10 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { const u32 address = decoded.shader_program_address(); - return fmt::format("Shader: %s, offset: 0x%x", CellGcmLocation{(address & 3) - 1}, address & ~3); + fmt::append(out, "Shader: %s, offset: 0x%x", CellGcmLocation{(address & 3) - 1}, address & ~3); } }; @@ -1801,7 +1829,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 transform_program_start() const { @@ -1809,9 +1837,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Transform program: start = " + std::to_string(decoded.transform_program_start()); + fmt::append(out, "Transform program: start: %u", decoded.transform_program_start()); } }; @@ -1824,7 +1852,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation context_dma() const { @@ -1832,9 +1860,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV406E semaphore: context: %s", decoded.context_dma()); + fmt::append(out, "NV406E semaphore: context: %s", decoded.context_dma()); } }; @@ -1848,7 +1876,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 semaphore_offset() const { @@ -1856,9 +1884,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV406E semaphore: offset: 0x%x", decoded.semaphore_offset()); + fmt::append(out, "NV406E semaphore: offset: 0x%x", decoded.semaphore_offset()); } }; @@ -1869,12 +1897,12 @@ struct registers_decoder { const u32 value; - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV406E semaphore: release: 0x%x", decoded.value); + fmt::append(out, "NV406E semaphore: release: 0x%x", decoded.value); } }; @@ -1885,12 +1913,12 @@ struct registers_decoder { const u32 value; - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV406E semaphore: acquire: 0x%x", decoded.value); + fmt::append(out, "NV406E semaphore: acquire: 0x%x", decoded.value); } }; @@ -1903,7 +1931,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation context_dma() const { @@ -1911,9 +1939,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV4097 semaphore: context: %s", decoded.context_dma()); + fmt::append(out, "NV4097 semaphore: context: %s", decoded.context_dma()); } }; @@ -1926,7 +1954,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 semaphore_offset() const { @@ -1934,9 +1962,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV4097 semaphore: offset: 0x%x", decoded.semaphore_offset()); + fmt::append(out, "NV4097 semaphore: offset: 0x%x", decoded.semaphore_offset()); } }; @@ -1949,7 +1977,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 input_offset() const { @@ -1957,9 +1985,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV3089: input offset: 0x%x", decoded.input_offset()); + fmt::append(out, "NV3089: input offset: 0x%x", decoded.input_offset()); } }; @@ -1972,7 +2000,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 output_offset() const { @@ -1980,9 +2008,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV3062: output offset: 0x%x", decoded.output_offset()); + fmt::append(out, "NV3062: output offset: 0x%x", decoded.output_offset()); } }; @@ -1995,7 +2023,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 offset() const { @@ -2003,9 +2031,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV309E: offset: 0x%x", decoded.offset()); + fmt::append(out, "NV309E: offset: 0x%x", decoded.offset()); } }; @@ -2018,7 +2046,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} // Convert signed fixed point 32-bit format f32 ds_dx() const @@ -2035,9 +2063,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "NV3089: dsdx = " + std::to_string(decoded.ds_dx()); + fmt::append(out, "NV3089: DS DX: %g", decoded.ds_dx()); } }; @@ -2050,7 +2078,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} // Convert signed fixed point 32-bit format f32 dt_dy() const @@ -2067,9 +2095,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "NV3089: dtdy = " + std::to_string(decoded.dt_dy()); + fmt::append(out, "NV3089: DT DY: %g", decoded.dt_dy()); } }; @@ -2082,7 +2110,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 input_pitch() const { @@ -2090,9 +2118,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "NV0039: input pitch = " + std::to_string(decoded.input_pitch()); + fmt::append(out, "NV0039: input pitch: %u", decoded.input_pitch()); } }; @@ -2105,7 +2133,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 output_pitch() const { @@ -2113,9 +2141,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "NV0039: output pitch = " + std::to_string(decoded.output_pitch()); + fmt::append(out, "NV0039: output pitch: %u", decoded.output_pitch()); } }; @@ -2128,7 +2156,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 input_line_length() const { @@ -2136,9 +2164,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "NV0039: line length input = " + std::to_string(decoded.input_line_length()); + fmt::append(out, "NV0039: line length input: %u", decoded.input_line_length()); } }; @@ -2151,7 +2179,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 line_count() const { @@ -2159,9 +2187,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "NV0039: line count = " + std::to_string(decoded.line_count()); + fmt::append(out, "NV0039: line count: %u", decoded.line_count()); } }; @@ -2174,7 +2202,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 output_offset() const { @@ -2182,9 +2210,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV0039: output offset: 0x%x", decoded.output_offset()); + fmt::append(out, "NV0039: output offset: 0x%x", decoded.output_offset()); } }; @@ -2197,7 +2225,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 input_offset() const { @@ -2205,9 +2233,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV0039: input offset: 00x%x", decoded.input_offset()); + fmt::append(out, "NV0039: input offset: 00x%x", decoded.input_offset()); } }; @@ -2220,7 +2248,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto depth_func() const { @@ -2228,9 +2256,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Depth: compare_function: %s", decoded.depth_func()); + fmt::append(out, "Depth: compare_function: %s", decoded.depth_func()); } }; @@ -2243,7 +2271,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto stencil_func() const { @@ -2251,9 +2279,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Stencil: (front) compare_function: %s", decoded.stencil_func()); + fmt::append(out, "Stencil: (front) compare_function: %s", decoded.stencil_func()); } }; @@ -2266,7 +2294,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto back_stencil_func() const { @@ -2274,9 +2302,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Stencil: back compare_function: %s", decoded.back_stencil_func()); + fmt::append(out, "Stencil: back compare_function: %s", decoded.back_stencil_func()); } }; @@ -2289,7 +2317,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto alpha_func() const { @@ -2297,9 +2325,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Alpha: compare_function: %s", decoded.alpha_func()); + fmt::append(out, "Alpha: compare_function: %s", decoded.alpha_func()); } }; @@ -2312,7 +2340,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto fail() const { @@ -2320,9 +2348,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Stencil: (front) fail op: %s", decoded.fail()); + fmt::append(out, "Stencil: (front) fail op: %s", decoded.fail()); } }; @@ -2335,7 +2363,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto zfail() const { @@ -2343,9 +2371,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Stencil: (front) zfail op: %s", decoded.zfail()); + fmt::append(out, "Stencil: (front) zfail op: %s", decoded.zfail()); } }; @@ -2358,7 +2386,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto zpass() const { @@ -2366,9 +2394,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Stencil: (front) zpass op: %s", decoded.zpass()); + fmt::append(out, "Stencil: (front) zpass op: %s", decoded.zpass()); } }; @@ -2381,7 +2409,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto back_fail() const { @@ -2389,9 +2417,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Stencil: (back) fail op: %s", decoded.back_fail()); + fmt::append(out, "Stencil: (back) fail op: %s", decoded.back_fail()); } }; @@ -2404,7 +2432,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto back_zfail() const { @@ -2412,9 +2440,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Stencil: (back) zfail op: %s", decoded.back_zfail()); + fmt::append(out, "Stencil: (back) zfail op: %s", decoded.back_zfail()); } }; @@ -2427,7 +2455,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto back_zpass() const { @@ -2435,9 +2463,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Stencil: (back) zpass op: %s", decoded.back_zpass()); + fmt::append(out, "Stencil: (back) zpass op: %s", decoded.back_zpass()); } }; @@ -2450,7 +2478,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u8 stencil_func_ref() const { @@ -2458,9 +2486,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Stencil: (front) func ref = " + std::to_string(decoded.stencil_func_ref()); + fmt::append(out, "Stencil: (front) func ref: %u", decoded.stencil_func_ref()); } }; @@ -2473,7 +2501,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u8 back_stencil_func_ref() const { @@ -2481,9 +2509,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Stencil: (back) func ref = " + std::to_string(decoded.back_stencil_func_ref()); + fmt::append(out, "Stencil: (back) func ref: %u", decoded.back_stencil_func_ref()); } }; @@ -2496,7 +2524,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u8 stencil_func_mask() const { @@ -2504,9 +2532,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Stencil: (front) func mask = " + std::to_string(decoded.stencil_func_mask()); + fmt::append(out, "Stencil: (front) func mask: %u", decoded.stencil_func_mask()); } }; @@ -2519,7 +2547,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u8 back_stencil_func_mask() const { @@ -2527,9 +2555,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Stencil: (back) func mask = " + std::to_string(decoded.back_stencil_func_mask()); + fmt::append(out, "Stencil: (back) func mask: %u", decoded.back_stencil_func_mask()); } }; @@ -2542,7 +2570,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 alpha_ref8() const { @@ -2560,10 +2588,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Alpha: ref unorm8 = " + std::to_string(decoded.alpha_ref8()) + - " f16 = " + std::to_string(decoded.alpha_ref16()); + fmt::append(out, "Alpha: ref unorm8: %g, f16: %g", decoded.alpha_ref8(), decoded.alpha_ref16()); } }; @@ -2576,7 +2603,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u8 blue() const { return bf_decoder<0, 8>(value); } u8 green() const { return bf_decoder<8, 8>(value); } @@ -2584,12 +2611,9 @@ struct registers_decoder u8 alpha() const { return bf_decoder<24, 8>(value); } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Clear: R = " + std::to_string(decoded.red()) + - " G = " + std::to_string(decoded.green()) + - " B = " + std::to_string(decoded.blue()) + - " A = " + std::to_string(decoded.alpha()); + fmt::append(out, "Clear: R = %u G = %u B = %u A = %u", decoded.red(), decoded.green(), decoded.blue(), decoded.alpha()); } }; @@ -2602,7 +2626,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u8 stencil_mask() const { @@ -2610,9 +2634,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Stencil: (front) mask = " + std::to_string(decoded.stencil_mask()); + fmt::append(out, "Stencil: (front) mask: %u", decoded.stencil_mask()); } }; @@ -2625,7 +2649,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u8 back_stencil_mask() const { @@ -2633,9 +2657,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Stencil: (back) mask = " + std::to_string(decoded.back_stencil_mask()); + fmt::append(out, "Stencil: (back) mask: %u", decoded.back_stencil_mask()); } }; @@ -2648,7 +2672,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto logic_operation() const { @@ -2656,9 +2680,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Logic: op: %s", decoded.logic_operation()); + fmt::append(out, "Logic: op: %s", decoded.logic_operation()); } }; @@ -2671,7 +2695,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto front_face_mode() const { @@ -2679,9 +2703,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Front Face: %s", decoded.front_face_mode()); + fmt::append(out, "Front Face: %s", decoded.front_face_mode()); } }; @@ -2695,7 +2719,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} cull_face cull_face_mode() const { @@ -2703,9 +2727,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Cull Face: %s", decoded.cull_face_mode()); + fmt::append(out, "Cull Face: %s", decoded.cull_face_mode()); } }; @@ -2718,7 +2742,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto target() const { @@ -2726,9 +2750,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Surface: Color target(s): %s", decoded.target()); + fmt::append(out, "Surface: Color target(s): %s", decoded.target()); } }; @@ -2741,7 +2765,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto fog_equation() const { @@ -2749,9 +2773,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Fog: %s", decoded.fog_equation()); + fmt::append(out, "Fog: %s", decoded.fog_equation()); } }; @@ -2764,7 +2788,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto primitive() const { @@ -2772,9 +2796,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Primitive: %s", decoded.primitive()); + fmt::append(out, "Primitive: %s", decoded.primitive()); } }; @@ -2787,7 +2811,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto transfer_op() const { @@ -2795,9 +2819,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV3089: op: %s", decoded.transfer_op()); + fmt::append(out, "NV3089: op: %s", decoded.transfer_op()); } }; @@ -2810,7 +2834,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto transfer_source_fmt() const { @@ -2818,9 +2842,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV3089: source fmt: %s", decoded.transfer_source_fmt()); + fmt::append(out, "NV3089: source fmt: %s", decoded.transfer_source_fmt()); } }; @@ -2833,7 +2857,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto ctx_surface() const { @@ -2841,9 +2865,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV3089: context surface: %s", decoded.ctx_surface()); + fmt::append(out, "NV3089: context surface: %s", decoded.ctx_surface()); } }; @@ -2856,7 +2880,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto transfer_dest_fmt() const { @@ -2864,9 +2888,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV3062: output fmt: %s", decoded.transfer_dest_fmt()); + fmt::append(out, "NV3062: output fmt: %s", decoded.transfer_dest_fmt()); } }; @@ -2888,7 +2912,7 @@ struct registers_decoder return bf_decoder<16, 16>(value); } public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto blend_rgb() const { @@ -2901,9 +2925,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Blend: equation rgb: %s a: %s", decoded.blend_rgb(), decoded.blend_a()); + fmt::append(out, "Blend: equation rgb: %s a: %s", decoded.blend_rgb(), decoded.blend_a()); } }; @@ -2925,7 +2949,7 @@ struct registers_decoder return bf_decoder<16, 16>(value); } public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto src_blend_rgb() const { @@ -2938,9 +2962,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Blend: sfactor rgb: %s a: %s", decoded.src_blend_rgb(), decoded.src_blend_a()); + fmt::append(out, "Blend: sfactor rgb: %s a: %s", decoded.src_blend_rgb(), decoded.src_blend_a()); } }; @@ -2962,7 +2986,7 @@ struct registers_decoder return bf_decoder<16, 16>(value); } public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto dst_blend_rgb() const { @@ -2975,9 +2999,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Blend: dfactor rgb: %s a: %s", decoded.dst_blend_rgb(), decoded.dst_blend_a()); + fmt::append(out, "Blend: dfactor rgb: %s a: %s", decoded.dst_blend_rgb(), decoded.dst_blend_a()); } }; @@ -2990,7 +3014,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool color_b() const { @@ -3018,12 +3042,10 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Surface: color mask A = " + print_boolean(decoded.color_a()) + - " R = " + print_boolean(decoded.color_r()) + - " G = " + print_boolean(decoded.color_g()) + - " B = " + print_boolean(decoded.color_b()); + fmt::append(out, "Surface: color mask A = %s R = %s G = %s B = %s" + , print_boolean(decoded.color_a()), print_boolean(decoded.color_r()), print_boolean(decoded.color_g()), print_boolean(decoded.color_b())); } }; @@ -3036,7 +3058,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool color_b(int index) const { @@ -3064,20 +3086,19 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - std::string result; + out += "Color Mask MRT:\n"; + for (int index = 1; index < 4; ++index) { - result += fmt::format("Surface[%d]: A:%d R:%d G:%d B:%d\n", + fmt::append(out, "Surface[%d]: A:%d R:%d G:%d B:%d\n", index, decoded.color_a(index), decoded.color_r(index), decoded.color_g(index), decoded.color_b(index)); } - - return "Color Mask MRT:\n" + result; } }; @@ -3093,7 +3114,7 @@ struct registers_decoder u8 window_shader_pixel_center_raw() const { return bf_decoder<16, 4>(value); } public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto window_shader_origin() const { @@ -3111,9 +3132,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Viewport: height: %u origin: %s pixel center: %s", decoded.window_shader_height() + fmt::append(out, "Viewport: height: %u origin: %s pixel center: %s", decoded.window_shader_height() , decoded.window_shader_origin(), decoded.window_shader_pixel_center()); } }; @@ -3127,7 +3148,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool blend_surface_b() const { @@ -3145,11 +3166,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Blend: mrt1 = " + print_boolean(decoded.blend_surface_b()) + - " mrt2 = " + print_boolean(decoded.blend_surface_c()) + - " mrt3 = " + print_boolean(decoded.blend_surface_d()); + fmt::append(out, "Blend: mrt1 = %s, mrt2 = %s, mrt3 = %s", print_boolean(decoded.blend_surface_b()), print_boolean(decoded.blend_surface_c()), print_boolean(decoded.blend_surface_d())); } }; @@ -3170,7 +3189,7 @@ struct registers_decoder u8 clip_plane5_raw() const { return bf_decoder<20, 4>(value); } public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto clip_plane0() const { @@ -3203,9 +3222,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("User clip: UC0: %s UC1: %s UC2: %s UC3: %s UC4: %s" + fmt::append(out, "User clip: UC0: %s UC1: %s UC2: %s UC3: %s UC4: %s" , decoded.clip_plane0() , decoded.clip_plane1() , decoded.clip_plane2() @@ -3224,7 +3243,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 line_width() const { @@ -3232,9 +3251,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Line width: " + std::to_string(decoded.line_width()); + fmt::append(out, "Line width: %g", decoded.line_width()); } }; @@ -3247,7 +3266,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 point_size() const { @@ -3255,9 +3274,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Point size: " + std::to_string(decoded.point_size()); + fmt::append(out, "Point size: %g", decoded.point_size()); } }; @@ -3270,7 +3289,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool enabled() const { @@ -3283,10 +3302,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Point sprite: enabled = " + print_boolean(decoded.enabled()) + - "override mask = " + fmt::format("0x%x", decoded.texcoord_mask()); + fmt::append(out, "Point sprite: enabled = %s, override mask = 0x%x", print_boolean(decoded.enabled()), decoded.texcoord_mask()); } }; @@ -3304,7 +3322,7 @@ struct registers_decoder u8 antialias_raw() const { return bf_decoder<12, 4>(value); } public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto color_fmt() const { @@ -3342,9 +3360,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Surface: Color format: %s DepthStencil format: %s Anti aliasing: %s w: %u h: %u", decoded.color_fmt() + fmt::append(out, "Surface: Color format: %s DepthStencil format: %s Anti aliasing: %s w: %u h: %u", decoded.color_fmt() , decoded.depth_fmt(), decoded.antialias(), decoded.log2width(), decoded.log2height()); } }; @@ -3361,7 +3379,7 @@ struct registers_decoder u32 clear_z24() const { return bf_decoder<8, 24>(value); } public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u8 clear_stencil() const { @@ -3377,11 +3395,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Clear: Z24 = " + std::to_string(decoded.clear_z(true)) + - " z16 = " + std::to_string(decoded.clear_z(false)) + - " Stencil = " + std::to_string(decoded.clear_stencil()); + fmt::append(out, "Clear: Z24 = %u, z16 = %u, Stencil = %u", decoded.clear_z(true), decoded.clear_z(false), decoded.clear_stencil()); } }; @@ -3396,7 +3412,7 @@ struct registers_decoder u8 type_raw() const { return bf_decoder<4, 8>(value); } public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation index_dma() const { @@ -3410,9 +3426,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Index: type: %s dma: %s", decoded.type(), decoded.index_dma()); + fmt::append(out, "Index: type: %s dma: %s", decoded.type(), decoded.index_dma()); } }; @@ -3425,7 +3441,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation dma_surface_a() const { @@ -3433,9 +3449,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Surface: A DMA: %s", decoded.dma_surface_a()); + fmt::append(out, "Surface: A DMA: %s", decoded.dma_surface_a()); } }; @@ -3448,7 +3464,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation dma_surface_b() const { @@ -3456,9 +3472,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Surface: B DMA: %s", decoded.dma_surface_b()); + fmt::append(out, "Surface: B DMA: %s", decoded.dma_surface_b()); } }; @@ -3471,7 +3487,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation dma_surface_c() const { @@ -3479,9 +3495,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Surface: C DMA: %s", decoded.dma_surface_c()); + fmt::append(out, "Surface: C DMA: %s", decoded.dma_surface_c()); } }; @@ -3494,7 +3510,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation dma_surface_d() const { @@ -3502,9 +3518,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Surface: D DMA: %s", decoded.dma_surface_d()); + fmt::append(out, "Surface: D DMA: %s", decoded.dma_surface_d()); } }; @@ -3517,7 +3533,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation dma_surface_z() const { @@ -3525,9 +3541,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Surface: Z DMA: %s", decoded.dma_surface_z()); + fmt::append(out, "Surface: Z DMA: %s", decoded.dma_surface_z()); } }; @@ -3540,7 +3556,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation context_dma() const { @@ -3548,9 +3564,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV3089: input DMA: %s", decoded.context_dma()); + fmt::append(out, "NV3089: input DMA: %s", decoded.context_dma()); } }; @@ -3563,7 +3579,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation output_dma() const { @@ -3571,9 +3587,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV3062: output DMA: %s", decoded.output_dma()); + fmt::append(out, "NV3062: output DMA: %s", decoded.output_dma()); } }; @@ -3586,7 +3602,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation context_dma() const { @@ -3594,9 +3610,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV309E: output DMA: %s", decoded.context_dma()); + fmt::append(out, "NV309E: output DMA: %s", decoded.context_dma()); } }; @@ -3609,7 +3625,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation output_dma() const { @@ -3617,9 +3633,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV0039: output DMA: %s", decoded.output_dma()); + fmt::append(out, "NV0039: output DMA: %s", decoded.output_dma()); } }; @@ -3632,7 +3648,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation input_dma() const { @@ -3640,9 +3656,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV0039: input DMA: %s", decoded.input_dma()); + fmt::append(out, "NV0039: input DMA: %s", decoded.input_dma()); } }; @@ -3655,7 +3671,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto context_dma_report() const { @@ -3663,9 +3679,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("REPORT: context DMA: %s", decoded.context_dma_report()); + fmt::append(out, "REPORT: context DMA: %s", decoded.context_dma_report()); } }; @@ -3678,7 +3694,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation context_dma_notify() const { @@ -3686,9 +3702,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NOTIFY: context DMA: %s, index: %d", decoded.context_dma_notify(), (decoded.context_dma_notify() & 7) ^ 7); + fmt::append(out, "NOTIFY: context DMA: %s, index: %u", decoded.context_dma_notify(), (decoded.context_dma_notify() & 7) ^ 7); } }; @@ -3704,7 +3720,7 @@ struct registers_decoder u8 transfer_interpolator_raw() const { return bf_decoder<24, 8>(value); } public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 format() const { @@ -3722,9 +3738,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV3089: input fmt: %u origin: %s interp: %s", decoded.format() + fmt::append(out, "NV3089: input fmt: %u origin: %s interp: %s", decoded.format() , decoded.transfer_origin(), decoded.transfer_interpolator()); } }; @@ -3739,7 +3755,7 @@ struct registers_decoder u32 transfer_destination_fmt() const { return bf_decoder<0, 16, u32>(value); } public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto format() const { @@ -3757,9 +3773,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV309E: output fmt: %s log2-width: %u log2-height: %u", decoded.format(), + fmt::append(out, "NV309E: output fmt: %s log2-width: %u log2-height: %u", decoded.format(), decoded.sw_width_log2(), decoded.sw_height_log2()); } }; @@ -3773,7 +3789,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u8 input_format() const { @@ -3790,10 +3806,9 @@ struct registers_decoder return std::make_tuple(static_cast(value & 0xff), static_cast(value >> 8)); } - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "NV0039: input format = " + std::to_string(decoded.input_format()) + - " output format = " + std::to_string(decoded.output_format()); + fmt::append(out, "NV0039: input format = %u, output format = %u", decoded.input_format(), decoded.output_format()); } }; @@ -3806,7 +3821,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 blue() const { @@ -3819,10 +3834,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Blend color: 16b BA = " + std::to_string(decoded.blue()) + - ", " + std::to_string(decoded.alpha()); + fmt::append(out, "Blend color: 16b BA = %u, %u", decoded.blue(), decoded.alpha()); } }; @@ -3835,7 +3849,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 red16() const { @@ -3868,11 +3882,10 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Blend color: 8b BGRA = " + - std::to_string(decoded.blue8()) + ", " + std::to_string(decoded.green8()) + ", " + std::to_string(decoded.red8()) + ", " + std::to_string(decoded.alpha8()) + - " 16b RG = " + std::to_string(decoded.red16()) + ", " + std::to_string(decoded.green16()); + fmt::append(out, "Blend color: 8b BGRA = %u, %u, %u, %u 16b RG = %u , %u" + , decoded.blue8(), decoded.green8(), decoded.red8(), decoded.alpha8(), decoded.red16(), decoded.green16()); } }; @@ -3895,7 +3908,7 @@ struct registers_decoder } public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} // x and y given as 16 bit fixed point @@ -3910,10 +3923,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "NV3089: in x = " + std::to_string(decoded.x()) + - " y = " + std::to_string(decoded.y()); + fmt::append(out, "NV3089: in x = %u y = %u", decoded.x(), decoded.y()); } }; @@ -3925,9 +3937,9 @@ struct registers_decoder decoded_type(u32) {} }; - static std::string dump(u32) + static void dump(std::string& out, u32) { - return "(nop)"; + out += "(nop)"; } }; @@ -3939,9 +3951,9 @@ struct registers_decoder decoded_type(u32) {} }; - static std::string dump(u32) + static void dump(std::string& out, u32) { - return "(invalidate vertex cache file)"; + out += "(invalidate vertex cache file)"; } }; @@ -3953,9 +3965,9 @@ struct registers_decoder decoded_type(u32) {} }; - static std::string dump(u32) + static void dump(std::string& out, u32) { - return "(invalidate vertex file)"; + out += "(invalidate vertex file)"; } }; @@ -3968,7 +3980,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool msaa_enabled() const { @@ -3991,12 +4003,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Anti_aliasing: " + print_boolean(decoded.msaa_enabled()) + - " alpha_to_coverage = " + print_boolean(decoded.msaa_alpha_to_coverage()) + - " alpha_to_one = " + print_boolean(decoded.msaa_alpha_to_one()) + - " sample_mask = " + std::to_string(decoded.msaa_sample_mask()); + fmt::append(out, "Anti_aliasing: %s alpha_to_coverage: %s alpha_to_one: %s sample_mask: %u", print_boolean(decoded.msaa_enabled()), print_boolean(decoded.msaa_alpha_to_coverage()), print_boolean(decoded.msaa_alpha_to_one()), decoded.msaa_sample_mask()); } }; @@ -4009,7 +4018,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto shading() const { @@ -4017,9 +4026,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Shading mode: %s", decoded.shading()); + fmt::append(out, "Shading mode: %s", decoded.shading()); } }; @@ -4032,7 +4041,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto front_polygon_mode() const { @@ -4040,9 +4049,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Front polygon mode: %s", decoded.front_polygon_mode()); + fmt::append(out, "Front polygon mode: %s", decoded.front_polygon_mode()); } }; @@ -4055,7 +4064,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto back_polygon_mode() const { @@ -4063,9 +4072,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("back polygon mode: %s", decoded.back_polygon_mode()); + fmt::append(out, "back polygon mode: %s", decoded.back_polygon_mode()); } }; @@ -4078,7 +4087,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 transform_constant_load() const { @@ -4086,9 +4095,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Transform constant load: %u", decoded.transform_constant_load()); + fmt::append(out, "Transform constant load: %u", decoded.transform_constant_load()); } }; @@ -4101,7 +4110,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool enabled() const { @@ -4109,9 +4118,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("polygon_stipple: %s", print_boolean(decoded.enabled())); + fmt::append(out, "polygon_stipple: %s", print_boolean(decoded.enabled())); } }; @@ -4124,7 +4133,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool enabled() const { @@ -4132,9 +4141,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("ZCULL: %s", print_boolean(decoded.enabled())); + fmt::append(out, "ZCULL: %s", print_boolean(decoded.enabled())); } }; @@ -4147,7 +4156,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool enabled() const { @@ -4155,9 +4164,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("ZCULL: stats %s", print_boolean(decoded.enabled())); + fmt::append(out, "ZCULL: stats %s", print_boolean(decoded.enabled())); } }; @@ -4170,7 +4179,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool enabled() const { @@ -4178,9 +4187,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("ZCULL: pixel count %s", print_boolean(decoded.enabled())); + fmt::append(out, "ZCULL: pixel count %s", print_boolean(decoded.enabled())); } }; @@ -4232,7 +4241,7 @@ struct transform_constant_helper u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 constant_value() const { @@ -4243,7 +4252,7 @@ struct transform_constant_helper static constexpr u32 reg = index / 4; static constexpr u8 subreg = index % 4; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { auto get_subreg_name = [](u8 subreg) -> std::string_view { @@ -4253,7 +4262,7 @@ struct transform_constant_helper "w"sv; }; - return fmt::format("TransformConstant[%u].%s: %g (0x%08x)", reg, get_subreg_name(subreg), decoded.constant_value(), std::bit_cast(decoded.constant_value())); + fmt::append(out, "TransformConstant[%u].%s: %g (0x%08x)", reg, get_subreg_name(subreg), decoded.constant_value(), std::bit_cast(decoded.constant_value())); } }; @@ -4269,12 +4278,12 @@ struct transform_program_helper { const u32 value; - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Transform Program (%u): 0x%08x", index, decoded.value); + fmt::append(out, "Transform Program (%u): 0x%08x", index, decoded.value); } }; @@ -4287,7 +4296,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 transform_program_load() const { @@ -4295,9 +4304,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Transform Program Load: %u", decoded.transform_program_load()); + fmt::append(out, "Transform Program Load: %u", decoded.transform_program_load()); } }; @@ -4315,7 +4324,7 @@ struct registers_decoder } public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 start() const { @@ -4328,10 +4337,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Draw vertexes range [" + std::to_string(decoded.start()) + ", " + - std::to_string(decoded.start() + decoded.count()) + "]"; + fmt::append(out, "Draw vertexes range [%u, %u]", decoded.start(), decoded.start() + decoded.count()); } }; @@ -4344,7 +4352,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 start() const { @@ -4357,10 +4365,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Draw vertexes range [IdxArray[" + std::to_string(decoded.start()) + - "], IdxArray[" + std::to_string(decoded.start() + decoded.count()) + "}]"; + fmt::append(out, "Draw vertexes range {IdxArray[%u], IdxArray[%u]}", decoded.start(), decoded.start() + decoded.count()); } }; @@ -4373,7 +4380,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool depth_float() const { @@ -4381,9 +4388,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Depth float: %s", print_boolean(decoded.depth_float())); + fmt::append(out, "Depth float: %s", print_boolean(decoded.depth_float())); } }; @@ -4404,7 +4411,7 @@ struct vertex_array_helper return bf_decoder<0, 3>(value); } public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 frequency() const { @@ -4427,12 +4434,9 @@ struct vertex_array_helper } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - if (decoded.size() == 0) - return fmt::format("Vertex Data Array %u (disabled)", index); - - auto print_vertex_attribute_format = [](rsx::vertex_base_type type) -> std::string + auto print_vertex_attribute_format = [](rsx::vertex_base_type type) -> std::string_view { switch (type) { @@ -4447,10 +4451,7 @@ struct vertex_array_helper fmt::throw_exception("Unexpected enum found"); }; - return "Vertex array " + std::to_string(index) + ": Type = " + print_vertex_attribute_format(decoded.type()) + - " size = " + std::to_string(decoded.size()) + - " stride = " + std::to_string(decoded.stride()) + - " frequency = " + std::to_string(decoded.frequency()); + fmt::append(out, "Vertex Data Array %u%s: Type: %s, size: %u, stride: %u, frequency: %u", index, decoded.size() ? "" : " (disabled)", print_vertex_attribute_format(decoded.type()), decoded.size(), decoded.stride(), decoded.frequency()); } }; @@ -4467,7 +4468,7 @@ struct vertex_array_offset_helper private: u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 offset() const { @@ -4475,9 +4476,9 @@ struct vertex_array_offset_helper } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Vertex array %u: Offset: 0x%x", index, decoded.offset()); + fmt::append(out, "Vertex Array %u: Offset: 0x%x", index, decoded.offset()); } }; @@ -4494,12 +4495,12 @@ struct register_vertex_printer { static std::string type() { - return "float" + std::to_string(count); + return fmt::format("float%u", count); } static std::string value(u32 v) { - return std::to_string(std::bit_cast(v)); + return fmt::format("%g", std::bit_cast(v)); } }; @@ -4508,26 +4509,26 @@ struct register_vertex_printer { static std::string type() { - return "short" + std::to_string(count); + return fmt::format("short%u", count); } static std::string value(u32 v) { - return std::to_string(v & 0xffff) + std::to_string(v >> 16); + return fmt::format("%u %u", (v & 0xffff), (v >> 16)); } }; template<> struct register_vertex_printer { - static std::string type() + static std::string_view type() { return "uchar4"; } - static std::string value(u32 v) + static std::string value(std::string& out, u32 v) { - return std::to_string(v & 0xff) + std::to_string((v >> 8) & 0xff) + std::to_string((v >> 16) & 0xff) + std::to_string((v >> 24) & 0xff); + return fmt::format("%u %u %u %u", (v & 0xff), ((v >> 8) & 0xff), ((v >> 16) & 0xff), ((v >> 24) & 0xff)); } }; @@ -4538,17 +4539,16 @@ struct register_vertex_helper { const u32 value; - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} }; static constexpr usz increment_per_array_index = (count * sizeof(type)) / sizeof(u32); static constexpr usz attribute_index = index / increment_per_array_index; static constexpr usz vertex_subreg = index % increment_per_array_index; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "register vertex " + std::to_string(attribute_index) + " as " + register_vertex_printer::type() + ": " + - register_vertex_printer::value(decoded.value); + fmt::append(out, "register vertex: %u as %u: %s", attribute_index, register_vertex_printer::type(), register_vertex_printer::value(decoded.value)); } }; @@ -4597,7 +4597,7 @@ struct texture_offset_helper { const u32 value; - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 offset() const { @@ -4605,9 +4605,9 @@ struct texture_offset_helper } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Texture %u: Offset: 0x%x", index, decoded.offset()); + fmt::append(out, "Texture %u: Offset: 0x%x", index, decoded.offset()); } }; @@ -4618,7 +4618,7 @@ struct texture_format_helper { const u32 value; - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation location() const { @@ -4653,9 +4653,9 @@ struct texture_format_helper } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Texture %u: %s, Cubemap: %s, %s, %s, Mipmap: %u", index, + fmt::append(out, "Texture %u: %s, Cubemap: %s, %s, %s, Mipmap: %u", index, decoded.location(), decoded.cubemap(), decoded.dimension(), decoded.format(), decoded.mipmap()); } }; @@ -4667,7 +4667,7 @@ struct texture_image_rect_helper { const u32 value; - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 height() const { @@ -4680,9 +4680,9 @@ struct texture_image_rect_helper } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Texture %u: W: %u, H: %u", index, decoded.width(), decoded.height()); + fmt::append(out, "Texture %u: W: %u, H: %u", index, decoded.width(), decoded.height()); } }; @@ -4693,7 +4693,7 @@ struct texture_control0_helper { const u32 value; - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool enabled() const { @@ -4721,9 +4721,9 @@ struct texture_control0_helper } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Texture %u: %s, Min/Max LOD: %g/%g, Max Aniso: %s, AKill: %s", index, print_boolean(decoded.enabled()) + fmt::append(out, "Texture %u: %s, Min/Max LOD: %g/%g, Max Aniso: %s, AKill: %s", index, print_boolean(decoded.enabled()) , decoded.min_lod(), decoded.max_lod(), decoded.max_aniso(), print_boolean(decoded.alpha_kill_enabled())); } }; @@ -4735,7 +4735,7 @@ struct texture_control3_helper { const u32 value; - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 depth() const { @@ -4748,9 +4748,9 @@ struct texture_control3_helper } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Texture %u: Pitch: %u, Depth: %u", index, decoded.pitch(), decoded.depth()); + fmt::append(out, "Texture %u: Pitch: %u, Depth: %u", index, decoded.pitch(), decoded.depth()); } }; @@ -4789,7 +4789,7 @@ struct vertex_texture_control0_helper { const u32 value; - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool enabled() const { @@ -4807,9 +4807,9 @@ struct vertex_texture_control0_helper } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("VTexture %u: %s, Min/Max LOD: %g/%g", index, print_boolean(decoded.enabled()) + fmt::append(out, "VTexture %u: %s, Min/Max LOD: %g/%g", index, print_boolean(decoded.enabled()) , decoded.min_lod(), decoded.max_lod()); } }; diff --git a/rpcs3/rpcs3qt/rsx_debugger.cpp b/rpcs3/rpcs3qt/rsx_debugger.cpp index 5d269d2761..9074ad7977 100644 --- a/rpcs3/rpcs3qt/rsx_debugger.cpp +++ b/rpcs3/rpcs3qt/rsx_debugger.cpp @@ -629,18 +629,21 @@ void rsx_debugger::GetMemory() const std::string dump; u32 cmd_i = 0; + std::string str; + for (const auto& command : frame_debug.command_queue) { - const std::string str = rsx::get_pretty_printing_function(command.first)(command.first, command.second); + str.clear(); + rsx::get_pretty_printing_function(command.first)(str, command.first, command.second); m_list_captured_frame->setItem(cmd_i++, 0, new QTableWidgetItem(qstr(str))); dump += str; dump += '\n'; } - if (fs::file file = fs::file(fs::get_cache_dir() + "command_dump.log", fs::rewrite)) + if (!dump.empty()) { - file.write(dump); + fs::write_file(fs::get_cache_dir() + "command_dump.log", fs::rewrite, dump); } for (u32 i = 0; i < frame_debug.draw_calls.size(); i++) From 1184d6aecbd758b7ab3471083fd96b9663759668 Mon Sep 17 00:00:00 2001 From: Eladash Date: Mon, 31 Jul 2023 11:34:23 +0300 Subject: [PATCH 010/184] Thread.cpp: Fixup SPU access violation log message --- Utilities/Thread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp index 3ab1566884..1988b4c990 100644 --- a/Utilities/Thread.cpp +++ b/Utilities/Thread.cpp @@ -1635,7 +1635,7 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe if (!g_tls_access_violation_recovered) { vm_log.notice("\n%s", dump_useful_thread_info()); - vm_log.error("[%s] Access violation %s location 0x%x (%s)", is_writing ? "writing" : "reading", cpu->get_name(), addr, (is_writing && vm::check_addr(addr)) ? "read-only memory" : "unmapped memory"); + vm_log.error("[%s] Access violation %s location 0x%x (%s)", cpu->get_name(), is_writing ? "writing" : "reading", addr, (is_writing && vm::check_addr(addr)) ? "read-only memory" : "unmapped memory"); } // TODO: From 365b2646565d3d5a5a95381be1850106ce91c673 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sun, 30 Jul 2023 23:25:57 +0200 Subject: [PATCH 011/184] cellMsgDialogAbort: do not return CELL_MSGDIALOG_ERROR_DIALOG_NOT_OPENED --- rpcs3/Emu/Cell/Modules/cellMsgDialog.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rpcs3/Emu/Cell/Modules/cellMsgDialog.cpp b/rpcs3/Emu/Cell/Modules/cellMsgDialog.cpp index 290c185e17..38e8179e4b 100644 --- a/rpcs3/Emu/Cell/Modules/cellMsgDialog.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMsgDialog.cpp @@ -533,13 +533,15 @@ error_code cellMsgDialogAbort() sysutil_send_system_cmd(CELL_SYSUTIL_DRAWING_END, 0); return CELL_OK; } + + return CELL_OK; // Not CELL_MSGDIALOG_ERROR_DIALOG_NOT_OPENED, tested on HW. } const auto dlg = g_fxo->get().get(); if (!dlg) { - return CELL_MSGDIALOG_ERROR_DIALOG_NOT_OPENED; + return CELL_OK; // Not CELL_MSGDIALOG_ERROR_DIALOG_NOT_OPENED, tested on HW. } if (!dlg->state.compare_and_swap_test(MsgDialogState::Open, MsgDialogState::Abort)) From 9b3a878c189e4e688b6025de0d0ff659116dcade Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 31 Jul 2023 00:47:09 +0200 Subject: [PATCH 012/184] cellAudioIn: reduce log spam Some logs I've seen are 20% filled with this stuff. --- rpcs3/Emu/Cell/Modules/cellAvconfExt.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellAvconfExt.cpp b/rpcs3/Emu/Cell/Modules/cellAvconfExt.cpp index 1ea8aa5f39..ed0e3d7526 100644 --- a/rpcs3/Emu/Cell/Modules/cellAvconfExt.cpp +++ b/rpcs3/Emu/Cell/Modules/cellAvconfExt.cpp @@ -202,7 +202,7 @@ error_code cellVideoOutSetupDisplay(u32 videoOut) error_code cellAudioInGetDeviceInfo(u32 deviceNumber, u32 deviceIndex, vm::ptr info) { - cellAvconfExt.todo("cellAudioInGetDeviceInfo(deviceNumber=0x%x, deviceIndex=0x%x, info=*0x%x)", deviceNumber, deviceIndex, info); + cellAvconfExt.trace("cellAudioInGetDeviceInfo(deviceNumber=0x%x, deviceIndex=0x%x, info=*0x%x)", deviceNumber, deviceIndex, info); if (deviceIndex != 0 || !info) { @@ -277,7 +277,7 @@ error_code cellVideoOutGetGamma(u32 videoOut, vm::ptr gamma) error_code cellAudioInGetAvailableDeviceInfo(u32 count, vm::ptr device_info) { - cellAvconfExt.todo("cellAudioInGetAvailableDeviceInfo(count=%d, info=*0x%x)", count, device_info); + cellAvconfExt.trace("cellAudioInGetAvailableDeviceInfo(count=%d, info=*0x%x)", count, device_info); if (count > 16 || !device_info) { From d119cf6e96cbd4df6f76bc05df3c6bd80863cc17 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 22 May 2021 10:42:05 +0200 Subject: [PATCH 013/184] Qt6 port --- .ci/build-linux.sh | 5 - .ci/build-mac.sh | 35 +++- .ci/install-freebsd.sh | 8 +- .ci/setup-windows.sh | 19 +- .cirrus.yml | 8 +- 3rdparty/qt5.cmake | 45 ---- 3rdparty/qt6.cmake | 45 ++++ BUILDING.md | 23 +- Utilities/bin_patch.cpp | 2 +- azure-pipelines.yml | 6 +- rpcs3/CMakeLists.txt | 32 +-- rpcs3/Emu/Cell/Modules/cellCamera.cpp | 1 - rpcs3/Emu/Io/camera_config.cpp | 8 +- rpcs3/Emu/Io/camera_config.h | 2 - rpcs3/Emu/Io/camera_handler_base.h | 3 +- rpcs3/Emu/RSX/display.h | 2 +- rpcs3/Input/basic_mouse_handler.cpp | 5 +- rpcs3/Input/keyboard_pad_handler.cpp | 13 +- rpcs3/main.cpp | 9 +- rpcs3/main_application.cpp | 2 +- rpcs3/qt/etc/qt.conf | 2 +- rpcs3/rpcs3.vcxproj | 21 +- rpcs3/rpcs3.vcxproj.filters | 4 +- rpcs3/rpcs3qt/CMakeLists.txt | 4 +- rpcs3/rpcs3qt/camera_settings_dialog.cpp | 165 +++++++-------- rpcs3/rpcs3qt/camera_settings_dialog.h | 4 +- rpcs3/rpcs3qt/camera_settings_dialog.ui | 6 +- rpcs3/rpcs3qt/config_checker.cpp | 6 +- rpcs3/rpcs3qt/custom_table_widget_item.cpp | 28 +-- rpcs3/rpcs3qt/gs_frame.h | 2 +- rpcs3/rpcs3qt/gui_application.cpp | 15 +- rpcs3/rpcs3qt/gui_application.h | 3 + rpcs3/rpcs3qt/log_viewer.cpp | 7 +- rpcs3/rpcs3qt/main_window.cpp | 30 +-- rpcs3/rpcs3qt/main_window.h | 4 +- rpcs3/rpcs3qt/memory_viewer_panel.cpp | 4 +- rpcs3/rpcs3qt/osk_dialog_frame.cpp | 2 +- rpcs3/rpcs3qt/progress_indicator.cpp | 18 +- rpcs3/rpcs3qt/progress_indicator.h | 4 +- rpcs3/rpcs3qt/qt_camera_error_handler.cpp | 60 +----- rpcs3/rpcs3qt/qt_camera_error_handler.h | 12 +- rpcs3/rpcs3qt/qt_camera_handler.cpp | 196 +++++++----------- rpcs3/rpcs3qt/qt_camera_handler.h | 12 +- ...o_surface.cpp => qt_camera_video_sink.cpp} | 73 ++----- ...video_surface.h => qt_camera_video_sink.h} | 12 +- rpcs3/rpcs3qt/qt_music_error_handler.cpp | 23 +- rpcs3/rpcs3qt/qt_music_error_handler.h | 4 +- rpcs3/rpcs3qt/qt_music_handler.cpp | 17 +- rpcs3/rpcs3qt/qt_utils.h | 2 +- rpcs3/rpcs3qt/register_editor_dialog.cpp | 2 +- rpcs3/rpcs3qt/save_manager_dialog.cpp | 1 - rpcs3/rpcs3qt/settings_dialog.cpp | 8 +- rpcs3/rpcs3qt/trophy_manager_dialog.cpp | 1 - rpcs3/rpcs3qt/user_manager_dialog.cpp | 1 - 54 files changed, 431 insertions(+), 595 deletions(-) delete mode 100644 3rdparty/qt5.cmake create mode 100644 3rdparty/qt6.cmake rename rpcs3/rpcs3qt/{qt_camera_video_surface.cpp => qt_camera_video_sink.cpp} (78%) rename rpcs3/rpcs3qt/{qt_camera_video_surface.h => qt_camera_video_sink.h} (66%) diff --git a/.ci/build-linux.sh b/.ci/build-linux.sh index 2728f2faad..a0e609efb1 100755 --- a/.ci/build-linux.sh +++ b/.ci/build-linux.sh @@ -1,10 +1,5 @@ #!/bin/sh -ex -# Setup Qt variables -export QT_BASE_DIR=/opt/qt"${QTVERMIN}" -export PATH="$QT_BASE_DIR"/bin:"$PATH" -export LD_LIBRARY_PATH="$QT_BASE_DIR"/lib/x86_64-linux-gnu:"$QT_BASE_DIR"/lib - if [ -z "$CIRRUS_CI" ]; then cd rpcs3 || exit 1 fi diff --git a/.ci/build-mac.sh b/.ci/build-mac.sh index 445df88ae2..8116d93484 100755 --- a/.ci/build-mac.sh +++ b/.ci/build-mac.sh @@ -28,23 +28,24 @@ export WORKDIR; WORKDIR="$(pwd)" # Get Qt -if [ ! -d "/tmp/Qt/5.15.2" ]; then +if [ ! -d "/tmp/Qt/$QT_VER" ]; then mkdir -p "/tmp/Qt" git clone https://github.com/engnr/qt-downloader.git cd qt-downloader git checkout f52efee0f18668c6d6de2dec0234b8c4bc54c597 cd "/tmp/Qt" "/opt/homebrew/bin/pipenv" run pip3 install py7zr requests semantic_version lxml - "/opt/homebrew/bin/pipenv" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop 5.15.2 clang_64 --opensource + mkdir -p "$QT_VER/macos" ; ln -s "macos" "$QT_VER/clang_64" + "/opt/homebrew/bin/pipenv" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia fi cd "$WORKDIR" -ditto "/tmp/Qt/5.15.2" "qt-downloader/5.15.2" +ditto "/tmp/Qt/$QT_VER" "qt-downloader/$QT_VER" -export Qt5_DIR="$WORKDIR/qt-downloader/5.15.2/clang_64/lib/cmake/Qt5" +export Qt6_DIR="$WORKDIR/qt-downloader/$QT_VER/clang_64/lib/cmake/Qt$QT_VER_MAIN" export SDL2_DIR="$BREW_X64_PATH/opt/sdl2/lib/cmake/SDL2" -export PATH="$BREW_X64_PATH/opt/llvm@16/bin:$WORKDIR/qt-downloader/5.15.2/clang_64/bin:$BREW_BIN:$BREW_SBIN:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Library/Apple/usr/bin:$PATH" +export PATH="$BREW_X64_PATH/opt/llvm@16/bin:$WORKDIR/qt-downloader/$QT_VER/clang_64/bin:$BREW_BIN:$BREW_SBIN:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Library/Apple/usr/bin:$PATH" export LDFLAGS="-L$BREW_X64_PATH/lib -Wl,-rpath,$BREW_X64_PATH/lib" export CPPFLAGS="-I$BREW_X64_PATH/include -msse -msse2 -mcx16 -no-pie" export LIBRARY_PATH="$BREW_X64_PATH/lib" @@ -67,16 +68,30 @@ sed -i '' "s/extern const double NSAppKitVersionNumber;/const double NSAppKitVer mkdir build && cd build || exit 1 "$BREW_X64_PATH/bin/cmake" .. \ - -DUSE_SDL=ON -DUSE_DISCORD_RPC=ON -DUSE_VULKAN=ON -DUSE_ALSA=OFF -DUSE_PULSE=OFF -DUSE_AUDIOUNIT=ON \ - -DLLVM_CCACHE_BUILD=OFF -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF \ - -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=OFF \ - -DLLVM_INCLUDE_UTILS=OFF -DLLVM_USE_PERF=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF \ + -DUSE_SDL=ON \ + -DUSE_DISCORD_RPC=ON \ + -DUSE_VULKAN=ON \ + -DUSE_ALSA=OFF \ + -DUSE_PULSE=OFF \ + -DUSE_AUDIOUNIT=ON \ + -DLLVM_CCACHE_BUILD=OFF \ + -DLLVM_BUILD_RUNTIME=OFF \ + -DLLVM_BUILD_TOOLS=OFF \ + -DLLVM_INCLUDE_DOCS=OFF \ + -DLLVM_INCLUDE_EXAMPLES=OFF \ + -DLLVM_INCLUDE_TESTS=OFF \ + -DLLVM_INCLUDE_TOOLS=OFF \ + -DLLVM_INCLUDE_UTILS=OFF \ + -DLLVM_USE_PERF=OFF \ + -DLLVM_ENABLE_Z3_SOLVER=OFF \ -DUSE_NATIVE_INSTRUCTIONS=OFF \ -DUSE_SYSTEM_MVK=ON \ -DUSE_SYSTEM_FAUDIO=ON \ -DUSE_SYSTEM_SDL=ON \ $CMAKE_EXTRA_OPTS \ - -DLLVM_TARGET_ARCH=X86_64 -DCMAKE_OSX_ARCHITECTURES=x86_64 -DCMAKE_IGNORE_PATH="$BREW_PATH/lib" \ + -DLLVM_TARGET_ARCH=X86_64 \ + -DCMAKE_OSX_ARCHITECTURES=x86_64 \ + -DCMAKE_IGNORE_PATH="$BREW_PATH/lib" \ -G Ninja "$BREW_PATH/bin/ninja"; build_status=$?; diff --git a/.ci/install-freebsd.sh b/.ci/install-freebsd.sh index 2a63e5ddfc..0692efc267 100755 --- a/.ci/install-freebsd.sh +++ b/.ci/install-freebsd.sh @@ -2,7 +2,7 @@ # NOTE: this script is run under root permissions # shellcheck shell=sh disable=SC2096 -# RPCS3 often needs recent Qt5 and Vulkan-Headers +# RPCS3 often needs recent Qt and Vulkan-Headers sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf export ASSUME_ALWAYS_YES=true @@ -11,8 +11,8 @@ pkg info # debug # Prefer newer Clang than in base system (see also .ci/build-freebsd.sh) pkg install llvm16 -# Mandatory dependencies (qt5-dbus and qt5-gui are pulled via qt5-widgets) -pkg install git ccache cmake ninja qt5-qmake qt5-buildtools qt5-widgets qt5-concurrent qt5-multimedia qt5-svg glew openal-soft ffmpeg +# Mandatory dependencies (qt6-base and qt6-svg are pulled via qt6-multimedia) +pkg install git ccache cmake ninja qt6-multimedia glew openal-soft ffmpeg -# Optional dependencies (libevdev is pulled by qt5-gui) +# Optional dependencies (libevdev is pulled by qt6-base) pkg install pkgconf alsa-lib pulseaudio sdl2 evdev-proto vulkan-headers vulkan-loader diff --git a/.ci/setup-windows.sh b/.ci/setup-windows.sh index adbf1e332f..d3bc02289b 100755 --- a/.ci/setup-windows.sh +++ b/.ci/setup-windows.sh @@ -11,25 +11,26 @@ PR_NUMBER="$SYSTEM_PULLREQUEST_PULLREQUESTID" QT_HOST="http://qt.mirror.constant.com/" QT_URL_VER=$(echo "$QT_VER" | sed "s/\.//g") QT_VER_MSVC_UP=$(echo "${QT_VER_MSVC}" | tr '[:lower:]' '[:upper:]') -QT_PREFIX="online/qtsdkrepository/windows_x86/desktop/qt${QT_VER_MAIN}_${QT_URL_VER}/qt.qt${QT_VER_MAIN}.${QT_URL_VER}.win64_${QT_VER_MSVC}_64/${QT_VER}-0-${QT_DATE}" -QT_SUFFIX="-Windows-Windows_10-${QT_VER_MSVC_UP}-Windows-Windows_10-X86_64.7z" -QT_BASE_URL="${QT_HOST}${QT_PREFIX}qtbase${QT_SUFFIX}" -QT_WINE_URL="${QT_HOST}${QT_PREFIX}qtwinextras${QT_SUFFIX}" -QT_DECL_URL="${QT_HOST}${QT_PREFIX}qtdeclarative${QT_SUFFIX}" -QT_TOOL_URL="${QT_HOST}${QT_PREFIX}qttools${QT_SUFFIX}" -QT_MM_URL="${QT_HOST}${QT_PREFIX}qtmultimedia${QT_SUFFIX}" -QT_SVG_URL="${QT_HOST}${QT_PREFIX}qtsvg${QT_SUFFIX}" +QT_PREFIX="online/qtsdkrepository/windows_x86/desktop/qt${QT_VER_MAIN}_${QT_URL_VER}/qt.qt${QT_VER_MAIN}.${QT_URL_VER}." +QT_PREFIX_2="win64_${QT_VER_MSVC}_64/${QT_VER}-0-${QT_DATE}" +QT_SUFFIX="-Windows-Windows_10_22H2-${QT_VER_MSVC_UP}-Windows-Windows_10_22H2-X86_64.7z" +QT_BASE_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtbase${QT_SUFFIX}" +QT_DECL_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtdeclarative${QT_SUFFIX}" +QT_TOOL_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qttools${QT_SUFFIX}" +QT_MM_URL="${QT_HOST}${QT_PREFIX}addons.qtmultimedia.${QT_PREFIX_2}qtmultimedia${QT_SUFFIX}" +QT_SVG_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtsvg${QT_SUFFIX}" +QT_5CMP_URL="${QT_HOST}${QT_PREFIX}qt5compat.${QT_PREFIX_2}qt5compat${QT_SUFFIX}" LLVMLIBS_URL='https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-16.0.1/llvmlibs_mt.7z' GLSLANG_URL='https://github.com/RPCS3/glslang/releases/download/custom-build-win/glslanglibs_mt.7z' VULKAN_SDK_URL="https://www.dropbox.com/s/cs77c3iv5mbo0bt/VulkanSDK-${VULKAN_VER}-Installer.exe" DEP_URLS=" \ $QT_BASE_URL \ - $QT_WINE_URL \ $QT_DECL_URL \ $QT_TOOL_URL \ $QT_MM_URL \ $QT_SVG_URL \ + $QT_5CMP_URL \ $LLVMLIBS_URL \ $GLSLANG_URL \ $VULKAN_SDK_URL" diff --git a/.cirrus.yml b/.cirrus.yml index 8e8ff80d1f..437c7dbe90 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -6,6 +6,8 @@ env: BUILD_SOURCEVERSION: $CIRRUS_CHANGE_IN_REPO BUILD_SOURCEBRANCHNAME: $CIRRUS_BRANCH RPCS3_TOKEN: ENCRYPTED[!a4c3850e29ab150692286a74bec29819d25971a7ec431b86de2a35f7ed90c5b2ab3c93469f9298e30924d843599110e9!] + QT_VER_MAIN: '6' + QT_VER: '6.5.2' windows_task: matrix: @@ -17,11 +19,9 @@ windows_task: env: CIRRUS_SHELL: "bash" COMPILER: msvc - QT_VER_MAIN: '5' BUILD_ARTIFACTSTAGINGDIRECTORY: ${CIRRUS_WORKING_DIR}\artifacts\ - QT_VER: '5.15.2' QT_VER_MSVC: 'msvc2019' - QT_DATE: '202011130602' + QT_DATE: '202307080351' QTDIR: C:\Qt\${QT_VER}\${QT_VER_MSVC}_64 VULKAN_VER: '1.3.224.1' VULKAN_SDK_SHA: '2029e652e39ee6a6036cff3765da31e1e6c595fd2413d3cd111dfab7855621ea' @@ -61,7 +61,7 @@ windows_task: linux_task: container: - image: rpcs3/rpcs3-ci-bionic:1.8 + image: rpcs3/rpcs3-ci-focal:1.0 cpu: 4 memory: 16G env: diff --git a/3rdparty/qt5.cmake b/3rdparty/qt5.cmake deleted file mode 100644 index d46e149ce4..0000000000 --- a/3rdparty/qt5.cmake +++ /dev/null @@ -1,45 +0,0 @@ -add_library(3rdparty_qt5 INTERFACE) - -set(QT_MIN_VER 5.15.2) - -find_package(Qt5 ${QT_MIN_VER} CONFIG COMPONENTS Widgets Concurrent Multimedia MultimediaWidgets Svg) -if(WIN32) - find_package(Qt5 ${QT_MIN_VER} COMPONENTS WinExtras REQUIRED) - target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::WinExtras Qt5::Concurrent Qt5::Multimedia Qt5::MultimediaWidgets Qt5::Svg) -else() - find_package(Qt5 ${QT_MIN_VER} COMPONENTS DBus Gui) - if(Qt5DBus_FOUND) - target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::DBus Qt5::Concurrent Qt5::Multimedia Qt5::MultimediaWidgets Qt5::Svg) - target_compile_definitions(3rdparty_qt5 INTERFACE -DHAVE_QTDBUS) - else() - target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::Concurrent Qt5::Multimedia Qt5::MultimediaWidgets Qt5::Svg) - endif() - target_include_directories(3rdparty_qt5 INTERFACE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) -endif() - -if(NOT Qt5Widgets_FOUND) - if(Qt5Widgets_VERSION VERSION_LESS ${QT_MIN_VER}) - message("Minimum supported Qt5 version is ${QT_MIN_VER}! You have version ${Qt5Widgets_VERSION} installed, please upgrade!") - if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - message(FATAL_ERROR "Most distros do not provide an up-to-date version of Qt. -If you're on Ubuntu or Linux Mint, there are PPAs you can use to install one of the latest qt5 versions. -Find the correct ppa at https://launchpad.net/~beineri and follow the instructions.") - elseif(WIN32) - message(FATAL_ERROR "You can download the latest version of Qt5 here: https://www.qt.io/download-open-source/") - else() - message(FATAL_ERROR "Look online for instructions on installing an up-to-date Qt5 on ${CMAKE_SYSTEM}.") - endif() - endif() - - message("CMake was unable to find Qt5!") - if(WIN32) - message(FATAL_ERROR "Make sure the QTDIR env variable has been set properly. (for example C:\\Qt\\${QT_MIN_VER}\\msvc2019_64\\) -You can also try setting the Qt5_DIR preprocessor definiton.") - elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") - message(FATAL_ERROR "Make sure to install your distro's qt5 package!") - else() - message(FATAL_ERROR "You need to have Qt5 installed, look online for instructions on installing Qt5 on ${CMAKE_SYSTEM}.") - endif() -endif() - -add_library(3rdparty::qt5 ALIAS 3rdparty_qt5) diff --git a/3rdparty/qt6.cmake b/3rdparty/qt6.cmake new file mode 100644 index 0000000000..900657b546 --- /dev/null +++ b/3rdparty/qt6.cmake @@ -0,0 +1,45 @@ +add_library(3rdparty_qt6 INTERFACE) + +set(QT_MIN_VER 6.4.0) + +find_package(Qt6 ${QT_MIN_VER} CONFIG COMPONENTS Widgets Concurrent Multimedia MultimediaWidgets Svg SvgWidgets) +if(WIN32) + find_package(Qt6 ${QT_MIN_VER} COMPONENTS WinExtras REQUIRED) + target_link_libraries(3rdparty_qt6 INTERFACE Qt6::Widgets Qt6::WinExtras Qt6::Concurrent Qt6::Multimedia Qt6::MultimediaWidgets Qt6::Svg Qt6::SvgWidgets) +else() + find_package(Qt6 ${QT_MIN_VER} COMPONENTS DBus Gui) + if(Qt6DBus_FOUND) + target_link_libraries(3rdparty_qt6 INTERFACE Qt6::Widgets Qt6::DBus Qt6::Concurrent Qt6::Multimedia Qt6::MultimediaWidgets Qt6::Svg Qt6::SvgWidgets) + target_compile_definitions(3rdparty_qt6 INTERFACE -DHAVE_QTDBUS) + else() + target_link_libraries(3rdparty_qt6 INTERFACE Qt6::Widgets Qt6::Concurrent Qt6::Multimedia Qt6::MultimediaWidgets Qt6::Svg Qt6::SvgWidgets) + endif() + target_include_directories(3rdparty_qt6 INTERFACE ${Qt6Gui_PRIVATE_INCLUDE_DIRS}) +endif() + +if(Qt6Widgets_FOUND) + if(Qt6Widgets_VERSION VERSION_LESS ${QT_MIN_VER}) + message("Minimum supported Qt version is ${QT_MIN_VER}! You have version ${Qt6Widgets_VERSION} installed, please upgrade!") + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + message(FATAL_ERROR "Most distros do not provide an up-to-date version of Qt. +If you're on Ubuntu or Linux Mint, there are PPAs you can use to install one of the latest qt6 versions. +Find the correct ppa at https://launchpad.net/~beineri and follow the instructions.") + elseif(WIN32) + message(FATAL_ERROR "You can download the latest version of Qt6 here: https://www.qt.io/download-open-source/") + else() + message(FATAL_ERROR "Look online for instructions on installing an up-to-date Qt6 on ${CMAKE_SYSTEM}.") + endif() + endif() +else() + message("CMake was unable to find Qt6!") + if(WIN32) + message(FATAL_ERROR "Make sure the QTDIR env variable has been set properly. (for example C:\\Qt\\${QT_MIN_VER}\\msvc2019_64\\) +You can also try setting the Qt6_DIR preprocessor definiton.") + elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") + message(FATAL_ERROR "Make sure to install your distro's qt6 package!") + else() + message(FATAL_ERROR "You need to have Qt6 installed, look online for instructions on installing Qt6 on ${CMAKE_SYSTEM}.") + endif() +endif() + +add_library(3rdparty::qt6 ALIAS 3rdparty_qt6) diff --git a/BUILDING.md b/BUILDING.md index e141ed189b..86da1c1fa5 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -9,11 +9,11 @@ Other instructions may be found [here](https://wiki.rpcs3.net/index.php?title=Bu * [CMake 3.16.9+](https://www.cmake.org/download/) (add to PATH) * [Python 3.6+](https://www.python.org/downloads/) (add to PATH) -* [Qt 5.15.2](https://www.qt.io/download-qt-installer) +* [Qt 6.5.2](https://www.qt.io/download-qt-installer) * [Visual Studio 2019](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community) * [Vulkan SDK 1.3.224+](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html)) -**Either add the** `QTDIR` **environment variable, e.g.** `\5.15.2\msvc2019_64\` **, or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2019)** +**Either add the** `QTDIR` **environment variable, e.g.** `\6.5.2\msvc2019_64\` **, or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2019)** ### Linux @@ -21,7 +21,7 @@ These are the essentials tools to build RPCS3 on Linux. Some of them can be inst * Clang 12+ or GCC 11+ * [CMake 3.16.9+](https://www.cmake.org/download/) -* [Qt 5.15.2](https://www.qt.io/download-qt-installer) +* [Qt 6.5.2](https://www.qt.io/download-qt-installer) * [Vulkan SDK 1.3.224+](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html)) * [SDL2](https://github.com/libsdl-org/SDL/releases) (for the FAudio backend) @@ -29,7 +29,7 @@ These are the essentials tools to build RPCS3 on Linux. Some of them can be inst #### Arch Linux - sudo pacman -S glew openal cmake vulkan-validation-layers qt5-base qt5-declarative qt5-multimedia sdl2 sndio jack2 base-devel + sudo pacman -S glew openal cmake vulkan-validation-layers qt6-base qt6-declarative qt6-multimedia sdl2 sndio jack2 base-devel #### Debian & Ubuntu @@ -38,14 +38,7 @@ These are the essentials tools to build RPCS3 on Linux. Some of them can be inst Ubuntu is usually horrendously out of date, and some packages need to be downloaded by hand. This part is for Qt, GCC, Vulkan, and CMake ##### Qt PPA -Ubuntu usually does not have a new enough Qt package to suit rpcs3's needs. There is a PPA available to work around this. Run the following: -``` -. /etc/os-release -sudo add-apt-repository ppa:beineri/opt-qt-5.15.2-$UBUNTU_CODENAME -sudo apt-get update -sudo apt-get install qt515base qt515svg -. /opt/qt515/bin/qt515-env.sh >/dev/null 2>&1 -``` +Ubuntu usually does not have a new enough Qt package to suit rpcs3's needs. There is currently no PPA available to work around this. ##### GCC 11.x installation @@ -82,11 +75,11 @@ sudo apt-get install cmake #### Fedora - sudo dnf install alsa-lib-devel cmake glew glew-devel libatomic libevdev-devel libudev-devel openal-devel qt5-qtbase-devel qt5-qtbase-private-devel vulkan-devel pipewire-jack-audio-connection-kit-devel qt5-qtmultimedia-devel qt5-qtsvg-devel + sudo dnf install alsa-lib-devel cmake glew glew-devel libatomic libevdev-devel libudev-devel openal-devel qt6-qtbase-devel qt6-qtbase-private-devel vulkan-devel pipewire-jack-audio-connection-kit-devel qt6-qtmultimedia-devel qt6-qtsvg-devel #### OpenSUSE - sudo zypper install git cmake libasound2 libpulse-devel openal-soft-devel glew-devel zlib-devel libedit-devel vulkan-devel libudev-devel libqt5-qtbase-devel libqt5-qtmultimedia-devel libqt5-qtsvg-devel libQt5Gui-private-headers-devel libevdev-devel libsndio7_1 libjack-devel + sudo zypper install git cmake libasound2 libpulse-devel openal-soft-devel glew-devel zlib-devel libedit-devel vulkan-devel libudev-devel libqt6-qtbase-devel libqt6-qtmultimedia-devel libqt6-qtsvg-devel libQt6Gui-private-headers-devel libevdev-devel libsndio7_1 libjack-devel ## Setup the project @@ -103,7 +96,7 @@ git submodule update --init #### Configuring the Qt plugin (if used) 1) Go to `Extensions->Qt VS Tools->Qt Versions`. -2) Add the path to your Qt installation with compiler e.g. `\5.15.2\msvc2019_64`, version will fill in automatically. +2) Add the path to your Qt installation with compiler e.g. `\6.5.2\msvc2019_64`, version will fill in automatically. 3) Go to `Extensions->Qt VS Tools->Options->Legacy Project Format`. 4) Set `Build: Run pre-build setup` to `true`. diff --git a/Utilities/bin_patch.cpp b/Utilities/bin_patch.cpp index a7ea0a6c94..2be0f84ce1 100644 --- a/Utilities/bin_patch.cpp +++ b/Utilities/bin_patch.cpp @@ -726,7 +726,7 @@ bool patch_engine::add_patch_data(YAML::Node node, patch_info& info, u32 modifie break; default: { - get_yaml_node_value(addr_node, error_message); + [[maybe_unused]] const u32 offset = get_yaml_node_value(addr_node, error_message); if (!error_message.empty()) { error_message = fmt::format("Skipping patch data entry: [ %s, 0x%.8x, %s ] (key: %s, location: %s) Invalid patch offset '%s' (not a valid u32 or overflow)", diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 67d18fe449..20af381970 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -65,10 +65,10 @@ jobs: - job: Windows_Build variables: COMPILER: msvc - QT_VER_MAIN: '5' - QT_VER: '5.15.2' + QT_VER_MAIN: '6' + QT_VER: '6.5.2' QT_VER_MSVC: 'msvc2019' - QT_DATE: '202011130602' + QT_DATE: '202307080351' QTDIR: C:\Qt\$(QT_VER)\$(QT_VER_MSVC)_64 VULKAN_VER: '1.3.224.1' VULKAN_SDK_SHA: '2029e652e39ee6a6036cff3765da31e1e6c595fd2413d3cd111dfab7855621ea' diff --git a/rpcs3/CMakeLists.txt b/rpcs3/CMakeLists.txt index a857f7bb0b..f89c8b059a 100644 --- a/rpcs3/CMakeLists.txt +++ b/rpcs3/CMakeLists.txt @@ -29,11 +29,11 @@ if(UNIX AND NOT APPLE) endif() endif() -# Qt5 +# Qt # finds Qt libraries and setups custom commands for MOC and UIC # Must be done here because generated MOC and UIC targets cant # be found otherwise -include(${CMAKE_SOURCE_DIR}/3rdparty/qt5.cmake) +include(${CMAKE_SOURCE_DIR}/3rdparty/qt6.cmake) # subdirectories add_subdirectory(Emu) @@ -84,7 +84,7 @@ set_target_properties(rpcs3 AUTOUIC ON) target_link_libraries(rpcs3 PRIVATE rpcs3_emu rpcs3_ui) -target_link_libraries(rpcs3 PRIVATE 3rdparty::discordRPC 3rdparty::qt5 3rdparty::hidapi 3rdparty::libusb 3rdparty::wolfssl 3rdparty::libcurl 3rdparty::zlib) +target_link_libraries(rpcs3 PRIVATE 3rdparty::discordRPC 3rdparty::qt6 3rdparty::hidapi 3rdparty::libusb 3rdparty::wolfssl 3rdparty::libcurl 3rdparty::zlib) target_link_libraries(rpcs3 PRIVATE ${ADDITIONAL_LIBS}) # Unix display manager @@ -112,7 +112,7 @@ if(USE_PRECOMPILED_HEADERS) target_precompile_headers(rpcs3 PRIVATE stdafx.h) endif() -get_target_property(_qmake_executable Qt5::qmake IMPORTED_LOCATION) +get_target_property(_qmake_executable Qt6::qmake IMPORTED_LOCATION) get_filename_component(_qt_bin_dir "${_qmake_executable}" DIRECTORY) if(APPLE) find_program(MACDEPLOYQT_EXECUTABLE macdeployqt HINTS "${_qt_bin_dir}") @@ -156,27 +156,27 @@ elseif(WIN32) # Qt installed from Qt installer has following hierarchy: # bin/ for release and debug dlls and windeployqt tools - # lib/cmake/Qt5/ for Qt5_Dir + # lib/cmake/Qt6/ for Qt6_Dir # Qt installed from vcpkg has following hierarchy: # bin/ for release dlls # debug/bin/ for debug dlls - # tools/qt5/bin/ for tools including windeployqt - # tools/qt5/debug/bin/ for tools with debug build including windeployqt - # share/cmake/Qt5/ for Qt5_Dir + # tools/qt6/bin/ for tools including windeployqt + # tools/qt6/debug/bin/ for tools with debug build including windeployqt + # share/cmake/Qt6/ for Qt6_Dir - # If Qt5 is installed from official Qt installer - # list(APPEND _QT5_TOOLS_PATHS "${Qt5_DIR}/../../../bin/") + # If Qt is installed from official Qt installer + # list(APPEND _QT6_TOOLS_PATHS "${Qt6_DIR}/../../../bin/") - # If Qt5 is installed from vcpkg - # list(APPEND _QT5_TOOLS_PATHS "${Qt5_DIR}/../../../tools/qt5$<$:/debug>/bin/") + # If Qt is installed from vcpkg + # list(APPEND _QT6_TOOLS_PATHS "${Qt6_DIR}/../../../tools/qt6$<$:/debug>/bin/") add_custom_command(TARGET rpcs3 POST_BUILD - # COMMAND set PATH=${_QT5_TOOLS_PATHS}$%PATH% + # COMMAND set PATH=${_QT6_TOOLS_PATHS}$%PATH% COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/bin" "$" - # If Qt5 is installed from vcpkg, add binary path to PATH + # If Qt is installed from vcpkg, add binary path to PATH # otherwise windeployqt tool won't be able to locate necessary dlls - # COMMAND set PATH=${Qt5_DIR}/../../../$<$:debug/>bin/$%PATH% - COMMAND "${WINDEPLOYQT_EXECUTABLE}" --no-angle --no-compiler-runtime --no-opengl-sw --no-patchqt --no-translations --no-quick --plugindir "$/qt/plugins" --verbose 0 "$") + # COMMAND set PATH=${Qt6_DIR}/../../../$<$:debug/>bin/$%PATH% + COMMAND "${WINDEPLOYQT_EXECUTABLE}" --no-compiler-runtime --no-opengl-sw --no-patchqt --no-translations --no-quick --no-system-d3d-compiler --no-quick-import --plugindir "$/qt6/plugins" --verbose 0 "$") endif() # Unix installation diff --git a/rpcs3/Emu/Cell/Modules/cellCamera.cpp b/rpcs3/Emu/Cell/Modules/cellCamera.cpp index 7e6a631d47..76a59c11c4 100644 --- a/rpcs3/Emu/Cell/Modules/cellCamera.cpp +++ b/rpcs3/Emu/Cell/Modules/cellCamera.cpp @@ -1891,7 +1891,6 @@ bool camera_context::on_handler_state(camera_handler_base::camera_handler_state { switch (state) { - case camera_handler_base::camera_handler_state::not_available: case camera_handler_base::camera_handler_state::closed: { if (is_attached) diff --git a/rpcs3/Emu/Io/camera_config.cpp b/rpcs3/Emu/Io/camera_config.cpp index bcc19c5786..dce03e8cd7 100644 --- a/rpcs3/Emu/Io/camera_config.cpp +++ b/rpcs3/Emu/Io/camera_config.cpp @@ -67,7 +67,7 @@ void cfg_camera::set_camera_setting(const std::string& camera, const camera_sett std::string cfg_camera::camera_setting::to_string() const { - return fmt::format("%d,%d,%f,%f,%d,%d,%d", width, height, min_fps, max_fps, format, pixel_aspect_width, pixel_aspect_height); + return fmt::format("%d,%d,%f,%f,%d", width, height, min_fps, max_fps, format); } void cfg_camera::camera_setting::from_string(const std::string& text) @@ -112,16 +112,12 @@ void cfg_camera::camera_setting::from_string(const std::string& text) !to_integer(::at32(list, 1), height) || !to_double(::at32(list, 2), min_fps) || !to_double(::at32(list, 3), max_fps) || - !to_integer(::at32(list, 4), format) || - !to_integer(::at32(list, 5), pixel_aspect_width) || - !to_integer(::at32(list, 6), pixel_aspect_height)) + !to_integer(::at32(list, 4), format)) { width = 0; height = 0; min_fps = 0; max_fps = 0; format = 0; - pixel_aspect_width = 0; - pixel_aspect_height = 0; } } diff --git a/rpcs3/Emu/Io/camera_config.h b/rpcs3/Emu/Io/camera_config.h index ca737f120e..07836a064d 100644 --- a/rpcs3/Emu/Io/camera_config.h +++ b/rpcs3/Emu/Io/camera_config.h @@ -15,8 +15,6 @@ struct cfg_camera final : cfg::node double min_fps = 0; double max_fps = 0; int format = 0; - int pixel_aspect_width = 0; - int pixel_aspect_height = 0; static const u32 member_count = 7; diff --git a/rpcs3/Emu/Io/camera_handler_base.h b/rpcs3/Emu/Io/camera_handler_base.h index adcd2653f1..1c0eb5588b 100644 --- a/rpcs3/Emu/Io/camera_handler_base.h +++ b/rpcs3/Emu/Io/camera_handler_base.h @@ -8,7 +8,6 @@ class camera_handler_base public: enum class camera_handler_state { - not_available, closed, open, running @@ -33,7 +32,7 @@ public: protected: std::mutex m_mutex; - atomic_t m_state = camera_handler_state::not_available; + atomic_t m_state = camera_handler_state::closed; bool m_mirrored = false; s32 m_format = 2; // CELL_CAMERA_RAW8 u32 m_bytesize = 0; diff --git a/rpcs3/Emu/RSX/display.h b/rpcs3/Emu/RSX/display.h index e6d5fa657e..8881275668 100644 --- a/rpcs3/Emu/RSX/display.h +++ b/rpcs3/Emu/RSX/display.h @@ -5,7 +5,7 @@ #elif defined(__APPLE__) // nothing #elif defined(HAVE_X11) -// Cannot include Xlib.h before Qt5 +// Cannot include Xlib.h before Qt // and we don't need all of Xlib anyway using Display = struct _XDisplay; using Window = unsigned long; diff --git a/rpcs3/Input/basic_mouse_handler.cpp b/rpcs3/Input/basic_mouse_handler.cpp index ac1c061aa9..9e06552244 100644 --- a/rpcs3/Input/basic_mouse_handler.cpp +++ b/rpcs3/Input/basic_mouse_handler.cpp @@ -128,6 +128,7 @@ void basic_mouse_handler::MouseMove(QMouseEvent* event) { // get the screen dimensions const QSize screen = m_target->size(); + const QPoint e_pos = event->pos(); if (m_target && m_target->isActive() && get_mouse_lock_state()) { @@ -144,7 +145,7 @@ void basic_mouse_handler::MouseMove(QMouseEvent* event) static QPoint p_real(p_center); // get the delta of the mouse position to the screen center - const QPoint p_delta = event->pos() - p_center; + const QPoint p_delta = e_pos - p_center; // update the current position without leaving the screen borders p_real.setX(std::clamp(p_real.x() + p_delta.x(), 0, screen.width())); @@ -155,7 +156,7 @@ void basic_mouse_handler::MouseMove(QMouseEvent* event) } else { - MouseHandlerBase::Move(event->x(), event->y(), screen.width(), screen.height()); + MouseHandlerBase::Move(e_pos.x(), e_pos.y(), screen.width(), screen.height()); } } } diff --git a/rpcs3/Input/keyboard_pad_handler.cpp b/rpcs3/Input/keyboard_pad_handler.cpp index ad1a725a21..713162d4fa 100644 --- a/rpcs3/Input/keyboard_pad_handler.cpp +++ b/rpcs3/Input/keyboard_pad_handler.cpp @@ -505,12 +505,13 @@ void keyboard_pad_handler::mouseMoveEvent(QMouseEvent* event) { static int last_pos_x = 0; static int last_pos_y = 0; + const QPoint e_pos = event->pos(); - movement_x = event->x() - last_pos_x; - movement_y = event->y() - last_pos_y; + movement_x = e_pos.x() - last_pos_x; + movement_y = e_pos.y() - last_pos_y; - last_pos_x = event->x(); - last_pos_y = event->y(); + last_pos_x = e_pos.x(); + last_pos_y = e_pos.y(); } else if (m_target && m_target->isActive()) { @@ -807,8 +808,8 @@ u32 keyboard_pad_handler::GetKeyCode(const QString& keyName) const QKeySequence seq(keyName); u32 key_code = Qt::NoButton; - if (seq.count() == 1 && seq[0] != Qt::Key_unknown) - key_code = seq[0]; + if (seq.count() == 1 && seq[0].key() != Qt::Key_unknown) + key_code = seq[0].key(); else input_log.notice("GetKeyCode(%s): seq.count() = %d", keyName, seq.count()); diff --git a/rpcs3/main.cpp b/rpcs3/main.cpp index 08716c4f42..a02fcf4bd7 100644 --- a/rpcs3/main.cpp +++ b/rpcs3/main.cpp @@ -1,4 +1,4 @@ -// Qt5.10+ frontend implementation for rpcs3. Known to work on Windows, Linux, Mac +// Qt6 frontend implementation for rpcs3. Known to work on Windows, Linux, Mac // by Sacha Refshauge, Megamouse and flash-fire #include @@ -349,9 +349,6 @@ QCoreApplication* create_application(int& argc, char* argv[]) use_high_dpi = "1" == qEnvironmentVariable("QT_ENABLE_HIGHDPI_SCALING", high_dpi_setting); } - // AA_EnableHighDpiScaling has to be set before creating a QApplication - QApplication::setAttribute(use_high_dpi ? Qt::AA_EnableHighDpiScaling : Qt::AA_DisableHighDpiScaling); - if (use_high_dpi) { // Set QT_SCALE_FACTOR_ROUNDING_POLICY from environment. Defaults to cli argument, which defaults to PassThrough. @@ -974,8 +971,6 @@ int main(int argc, char** argv) if (gui_application* gui_app = qobject_cast(app.data())) { - gui_app->setAttribute(Qt::AA_UseHighDpiPixmaps); - gui_app->setAttribute(Qt::AA_DisableWindowContextHelpButton); gui_app->setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); gui_app->SetShowGui(!s_no_gui); @@ -1057,7 +1052,7 @@ int main(int argc, char** argv) bool got_timer_resolution = NtQueryTimerResolution(&min_res, &max_res, &orig_res) == 0; // Set 0.5 msec timer resolution for best performance - // - As QT5 timers (QTimer) sets the timer resolution to 1 msec, override it here. + // - As QT timers (QTimer) sets the timer resolution to 1 msec, override it here. if (parser.value(arg_timer).toStdString() == "1") { ULONG new_res; diff --git a/rpcs3/main_application.cpp b/rpcs3/main_application.cpp index 35100e327e..c3d37658ba 100644 --- a/rpcs3/main_application.cpp +++ b/rpcs3/main_application.cpp @@ -315,7 +315,7 @@ EmuCallbacks main_application::CreateCallbacks() image = image.convertToFormat(QImage::Format::Format_RGBA8888); } - std::memcpy(dst, image.constBits(), std::min(4 * target_width * target_height, image.height() * image.bytesPerLine())); + std::memcpy(dst, image.constBits(), std::min(target_width * target_height * 4LL, image.height() * image.bytesPerLine())); success = true; sys_log.notice("get_scaled_image scaled image: path='%s', width=%d, height=%d", path, width, height); } diff --git a/rpcs3/qt/etc/qt.conf b/rpcs3/qt/etc/qt.conf index 8a60d8561e..af6127460f 100644 --- a/rpcs3/qt/etc/qt.conf +++ b/rpcs3/qt/etc/qt.conf @@ -1,4 +1,4 @@ [Paths] -Prefix = qt/ +Prefix = qt6/ Plugins = plugins Translations = translations diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index dd6818ca48..dd21034866 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -1,4 +1,4 @@ - + @@ -71,8 +71,8 @@ - ..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\cubeb\extra;..\3rdparty\cubeb\cubeb\include\;..\3rdparty\flatbuffers\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\curl\curl\include;..\3rdparty\rtmidi\rtmidi;..\3rdparty\libusb\libusb\libusb;$(VULKAN_SDK)\Include;..\3rdparty\libsdl-org\SDL\include;..\3rdparty\XAudio2Redist\include;$(QTDIR)\include;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtCore;.\release;$(QTDIR)\mkspecs\win32-msvc2015;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtConcurrent;$(QTDIR)\include\QtMultimedia;$(QTDIR)\include\QtMultimediaWidgets;$(QTDIR)\include\QtSvg;%(AdditionalIncludeDirectories) - -Zc:strictStrings -Zc:throwingNew- -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) + ..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\cubeb\extra;..\3rdparty\cubeb\cubeb\include\;..\3rdparty\flatbuffers\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\curl\curl\include;..\3rdparty\rtmidi\rtmidi;..\3rdparty\libusb\libusb\libusb;$(VULKAN_SDK)\Include;..\3rdparty\libsdl-org\SDL\include;..\3rdparty\XAudio2Redist\include;$(QTDIR)\include;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtCore5Compat;$(QTDIR)\include\QtConcurrent;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtSvg;$(QTDIR)\include\QtSvgWidgets;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtMultimedia;$(QTDIR)\mkspecs\win32-msvc;.\release;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;%(AdditionalIncludeDirectories) + /Zc:__cplusplus -Zc:strictStrings -Zc:throwingNew- -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) release\ false ProgramDatabase @@ -89,7 +89,7 @@ TurnOffAllWarnings - DbgHelp.lib;Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;miniupnpc_static.lib;rtmidi.lib;imm32.lib;ksuser.lib;version.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslang.lib;OSDependent.lib;OGLCompiler.lib;SPIRV.lib;MachineIndependent.lib;GenericCodeGen.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;$(QTDIR)\lib\qtmain.lib;shell32.lib;$(QTDIR)\lib\Qt5Widgets.lib;$(QTDIR)\lib\Qt5Gui.lib;$(QTDIR)\lib\Qt5Core.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;Qt5WinExtras.lib;Qt5Concurrent.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;Qt5Multimedia.lib;Qt5MultimediaWidgets.lib;Qt5Svg.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;SDL.lib;%(AdditionalDependencies) + DbgHelp.lib;Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;miniupnpc_static.lib;rtmidi.lib;imm32.lib;ksuser.lib;version.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslang.lib;OSDependent.lib;OGLCompiler.lib;SPIRV.lib;MachineIndependent.lib;GenericCodeGen.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;shell32.lib;Qt6Core.lib;Qt6Gui.lib;Qt6Widgets.lib;Qt6Concurrent.lib;Qt6Core5Compat.lib;Qt6Multimedia.lib;Qt6MultimediaWidgets.lib;Qt6Svg.lib;Qt6SvgWidgets.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;SDL.lib;%(AdditionalDependencies) ..\3rdparty\OpenAL\libs\Win64;..\3rdparty\glslang\build\hlsl\Release;..\3rdparty\glslang\build\SPIRV\Release;..\3rdparty\glslang\build\OGLCompilersDLL\Release;..\3rdparty\glslang\build\glslang\OSDependent\Windows\Release;..\3rdparty\glslang\build\glslang\Release;..\3rdparty\SPIRV\build\source\Release;..\3rdparty\SPIRV\build\source\opt\Release;..\lib\$(CONFIGURATION)-$(PLATFORM);..\3rdparty\XAudio2Redist\libs;..\3rdparty\discord-rpc\lib;$(QTDIR)\lib;%(AdditionalLibraryDirectories);$(VULKAN_SDK)\Lib "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) true @@ -102,6 +102,7 @@ true 0x10000 xaudio2_9redist.dll + mainCRTStartup Unsigned @@ -113,7 +114,7 @@ - $(QTDIR)\bin\windeployqt --no-angle --no-opengl-sw --no-translations --no-quick --plugindir "$(TargetDir)qt\plugins" --release "$(TargetPath)" + $(QTDIR)\bin\windeployqt --no-compiler-runtime --no-opengl-sw --no-patchqt --no-translations --no-quick --no-system-d3d-compiler --no-quick-import --plugindir "$(TargetDir)qt6\plugins" --release "$(TargetPath)" @@ -123,7 +124,7 @@ - ..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\cubeb\extra;..\3rdparty\cubeb\cubeb\include\;..\3rdparty\flatbuffers\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\curl\curl\include;..\3rdparty\rtmidi\rtmidi;..\3rdparty\libusb\libusb\libusb;$(VULKAN_SDK)\Include;..\3rdparty\XAudio2Redist\include;$(QTDIR)\include;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtCore;.\debug;$(QTDIR)\mkspecs\win32-msvc2015;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtConcurrent;$(QTDIR)\include\QtMultimedia;$(QTDIR)\include\QtMultimediaWidgets;$(QTDIR)\include\QtSvg;%(AdditionalIncludeDirectories) + ..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\cubeb\extra;..\3rdparty\cubeb\cubeb\include\;..\3rdparty\flatbuffers\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\curl\curl\include;..\3rdparty\rtmidi\rtmidi;..\3rdparty\libusb\libusb\libusb;$(VULKAN_SDK)\Include;..\3rdparty\XAudio2Redist\include;$(QTDIR)\include;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtCore5Compat;$(QTDIR)\include\QtConcurrent;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtSvg;$(QTDIR)\include\QtSvgWidgets;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtMultimedia;$(QTDIR)\mkspecs\win32-msvc;.\debug;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;%(AdditionalIncludeDirectories) -Zc:strictStrings -Zc:throwingNew- -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) debug\ false @@ -140,7 +141,7 @@ $(IntDir)vc$(PlatformToolsetVersion).pdb - DbgHelp.lib;Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;miniupnpc_static.lib;rtmidi.lib;ksuser.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslangd.lib;OSDependentd.lib;OGLCompilerd.lib;SPIRVd.lib;MachineIndependentd.lib;GenericCodeGend.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;$(QTDIR)\lib\qtmaind.lib;shell32.lib;$(QTDIR)\lib\Qt5Widgetsd.lib;$(QTDIR)\lib\Qt5Guid.lib;$(QTDIR)\lib\Qt5Cored.lib;Qt5Cored.lib;Qt5Guid.lib;Qt5Widgetsd.lib;Qt5WinExtrasd.lib;Qt5Concurrentd.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;Qt5Multimediad.lib;Qt5MultimediaWidgetsd.lib;Qt5Svgd.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;%(AdditionalDependencies) + DbgHelp.lib;Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;miniupnpc_static.lib;rtmidi.lib;ksuser.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslangd.lib;OSDependentd.lib;OGLCompilerd.lib;SPIRVd.lib;MachineIndependentd.lib;GenericCodeGend.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;shell32.lib;qtmaind.lib;Qt6Cored.lib;Qt6Guid.lib;Qt6Widgetsd.lib;Qt6Concurrentd.lib;Qt6Multimediad.lib;Qt6MultimediaWidgetsd.lib;Qt6Svgd.lib;Qt6SvgWidgetsd.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;%(AdditionalDependencies) ..\3rdparty\OpenAL\libs\Win64;..\3rdparty\glslang\build\hlsl\Debug;..\3rdparty\glslang\build\SPIRV\Debug;..\3rdparty\glslang\build\OGLCompilersDLL\Debug;..\3rdparty\glslang\build\glslang\OSDependent\Windows\Debug;..\3rdparty\glslang\build\glslang\Debug;..\3rdparty\SPIRV\build\source\opt\Debug;..\3rdparty\XAudio2Redist\libs;..\3rdparty\discord-rpc\lib;..\lib\$(CONFIGURATION)-$(PLATFORM);$(QTDIR)\lib;%(AdditionalLibraryDirectories);$(VULKAN_SDK)\Lib "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" /VERBOSE %(AdditionalOptions) true @@ -165,7 +166,7 @@ - $(QTDIR)\bin\windeployqt --no-angle --no-opengl-sw --no-translations --no-quick --plugindir "$(TargetDir)qt\plugins" --debug "$(TargetPath)" + $(QTDIR)\bin\windeployqt --no-compiler-runtime --no-opengl-sw --no-patchqt --no-translations --no-quick --no-system-d3d-compiler --no-quick-import --plugindir "$(TargetDir)qt6\plugins" --debug "$(TargetPath)" @@ -763,7 +764,7 @@ - + @@ -1482,7 +1483,7 @@ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" - + $(QTDIR)\bin\moc.exe;%(FullPath) Moc%27ing %(Identity)... diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index 76d205b086..4de7e725a5 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -807,7 +807,7 @@ Io\camera - + Io\camera @@ -1166,7 +1166,7 @@ Generated Files - + Io\camera diff --git a/rpcs3/rpcs3qt/CMakeLists.txt b/rpcs3/rpcs3qt/CMakeLists.txt index 72afb6893f..45abc35bf8 100644 --- a/rpcs3/rpcs3qt/CMakeLists.txt +++ b/rpcs3/rpcs3qt/CMakeLists.txt @@ -65,7 +65,7 @@ add_library(rpcs3_ui STATIC progress_indicator.cpp qt_camera_error_handler.cpp qt_camera_handler.cpp - qt_camera_video_surface.cpp + qt_camera_video_sink.cpp qt_music_error_handler.cpp qt_music_handler.cpp qt_utils.cpp @@ -140,7 +140,7 @@ target_compile_definitions(rpcs3_ui PRIVATE WIN32_LEAN_AND_MEAN) target_link_libraries(rpcs3_ui PUBLIC - 3rdparty::qt5 3rdparty::yaml-cpp + 3rdparty::qt6 3rdparty::yaml-cpp PRIVATE rpcs3_emu diff --git a/rpcs3/rpcs3qt/camera_settings_dialog.cpp b/rpcs3/rpcs3qt/camera_settings_dialog.cpp index db2d4c6b28..89f72cef8d 100644 --- a/rpcs3/rpcs3qt/camera_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/camera_settings_dialog.cpp @@ -3,61 +3,56 @@ #include "ui_camera_settings_dialog.h" #include "Emu/Io/camera_config.h" -#include +#include +#include #include #include LOG_CHANNEL(camera_log, "Camera"); template <> -void fmt_class_string::format(std::string& out, u64 arg) +void fmt_class_string::format(std::string& out, u64 arg) { - format_enum(out, arg, [](QVideoFrame::PixelFormat value) + format_enum(out, arg, [](QVideoFrameFormat::PixelFormat value) { switch (value) { - case QVideoFrame::Format_Invalid: return "Invalid"; - case QVideoFrame::Format_ARGB32: return "ARGB32"; - case QVideoFrame::Format_ARGB32_Premultiplied: return "ARGB32_Premultiplied"; - case QVideoFrame::Format_RGB32: return "RGB32"; - case QVideoFrame::Format_RGB24: return "RGB24"; - case QVideoFrame::Format_RGB565: return "RGB565"; - case QVideoFrame::Format_RGB555: return "RGB555"; - case QVideoFrame::Format_ARGB8565_Premultiplied: return "ARGB8565_Premultiplied"; - case QVideoFrame::Format_BGRA32: return "BGRA32"; - case QVideoFrame::Format_BGRA32_Premultiplied: return "BGRA32_Premultiplied"; - case QVideoFrame::Format_BGR32: return "BGR32"; - case QVideoFrame::Format_BGR24: return "BGR24"; - case QVideoFrame::Format_BGR565: return "BGR565"; - case QVideoFrame::Format_BGR555: return "BGR555"; - case QVideoFrame::Format_BGRA5658_Premultiplied: return "BGRA5658_Premultiplied"; - case QVideoFrame::Format_AYUV444: return "AYUV444"; - case QVideoFrame::Format_AYUV444_Premultiplied: return "AYUV444_Premultiplied"; - case QVideoFrame::Format_YUV444: return "YUV444"; - case QVideoFrame::Format_YUV420P: return "YUV420P"; - case QVideoFrame::Format_YV12: return "YV12"; - case QVideoFrame::Format_UYVY: return "UYVY"; - case QVideoFrame::Format_YUYV: return "YUYV"; - case QVideoFrame::Format_NV12: return "NV12"; - case QVideoFrame::Format_NV21: return "NV21"; - case QVideoFrame::Format_IMC1: return "IMC1"; - case QVideoFrame::Format_IMC2: return "IMC2"; - case QVideoFrame::Format_IMC3: return "IMC3"; - case QVideoFrame::Format_IMC4: return "IMC4"; - case QVideoFrame::Format_Y8: return "Y8"; - case QVideoFrame::Format_Y16: return "Y16"; - case QVideoFrame::Format_Jpeg: return "Jpeg"; - case QVideoFrame::Format_CameraRaw: return "CameraRaw"; - case QVideoFrame::Format_AdobeDng: return "AdobeDng"; - case QVideoFrame::Format_ABGR32: return "ABGR32"; - case QVideoFrame::Format_YUV422P: return "YUV422P"; - case QVideoFrame::Format_User: return "User"; + case QVideoFrameFormat::Format_ARGB8888: return "ARGB8888"; + case QVideoFrameFormat::Format_ARGB8888_Premultiplied: return "ARGB8888_Premultiplied"; + case QVideoFrameFormat::Format_XRGB8888: return "XRGB8888"; + case QVideoFrameFormat::Format_BGRA8888: return "BGRA8888"; + case QVideoFrameFormat::Format_BGRA8888_Premultiplied: return "BGRA8888_Premultiplied"; + case QVideoFrameFormat::Format_BGRX8888: return "BGRX8888"; + case QVideoFrameFormat::Format_ABGR8888: return "ABGR8888"; + case QVideoFrameFormat::Format_XBGR8888: return "XBGR8888"; + case QVideoFrameFormat::Format_RGBA8888: return "RGBA8888"; + case QVideoFrameFormat::Format_RGBX8888: return "RGBX8888"; + case QVideoFrameFormat::Format_AYUV: return "AYUV"; + case QVideoFrameFormat::Format_AYUV_Premultiplied: return "AYUV_Premultiplied"; + case QVideoFrameFormat::Format_YUV420P: return "YUV420P"; + case QVideoFrameFormat::Format_YUV422P: return "YUV422P"; + case QVideoFrameFormat::Format_YV12: return "YV12"; + case QVideoFrameFormat::Format_UYVY: return "UYVY"; + case QVideoFrameFormat::Format_YUYV: return "YUYV"; + case QVideoFrameFormat::Format_NV12: return "NV12"; + case QVideoFrameFormat::Format_NV21: return "NV21"; + case QVideoFrameFormat::Format_IMC1: return "IMC1"; + case QVideoFrameFormat::Format_IMC2: return "IMC2"; + case QVideoFrameFormat::Format_IMC3: return "IMC3"; + case QVideoFrameFormat::Format_IMC4: return "IMC4"; + case QVideoFrameFormat::Format_Y8: return "Y8"; + case QVideoFrameFormat::Format_Y16: return "Y16"; + case QVideoFrameFormat::Format_P010: return "P010"; + case QVideoFrameFormat::Format_P016: return "P016"; + case QVideoFrameFormat::Format_SamplerExternalOES: return "SamplerExternalOES"; + case QVideoFrameFormat::Format_Jpeg: return "Jpeg"; + case QVideoFrameFormat::Format_SamplerRect: return "SamplerRect"; default: return unknown; } }); } -Q_DECLARE_METATYPE(QCameraInfo); +Q_DECLARE_METATYPE(QCameraDevice); camera_settings_dialog::camera_settings_dialog(QWidget* parent) : QDialog(parent) @@ -67,7 +62,7 @@ camera_settings_dialog::camera_settings_dialog(QWidget* parent) load_config(); - for (const QCameraInfo& camera_info : QCameraInfo::availableCameras()) + for (const QCameraDevice& camera_info : QMediaDevices::videoInputs()) { if (camera_info.isNull()) continue; ui->combo_camera->addItem(camera_info.description(), QVariant::fromValue(camera_info)); @@ -108,13 +103,13 @@ camera_settings_dialog::~camera_settings_dialog() void camera_settings_dialog::handle_camera_change(int index) { - if (index < 0 || !ui->combo_camera->itemData(index).canConvert()) + if (index < 0 || !ui->combo_camera->itemData(index).canConvert()) { ui->combo_settings->clear(); return; } - const QCameraInfo camera_info = ui->combo_camera->itemData(index).value(); + const QCameraDevice camera_info = ui->combo_camera->itemData(index).value(); if (camera_info.isNull()) { @@ -123,7 +118,9 @@ void camera_settings_dialog::handle_camera_change(int index) } m_camera.reset(new QCamera(camera_info)); - m_camera->setViewfinder(ui->viewfinder); + m_media_capture_session.reset(new QMediaCaptureSession(nullptr)); + m_media_capture_session->setCamera(m_camera.get()); + m_media_capture_session->setVideoSink(ui->videoWidget->videoSink()); if (!m_camera->isAvailable()) { @@ -132,42 +129,34 @@ void camera_settings_dialog::handle_camera_change(int index) return; } - m_camera->load(); - ui->combo_settings->blockSignals(true); ui->combo_settings->clear(); - QList settings = m_camera->supportedViewfinderSettings(); - std::sort(settings.begin(), settings.end(), [](const QCameraViewfinderSettings& l, const QCameraViewfinderSettings& r) -> bool + QList settings = camera_info.videoFormats(); + std::sort(settings.begin(), settings.end(), [](const QCameraFormat& l, const QCameraFormat& r) -> bool { if (l.resolution().width() > r.resolution().width()) return true; if (l.resolution().width() < r.resolution().width()) return false; if (l.resolution().height() > r.resolution().height()) return true; if (l.resolution().height() < r.resolution().height()) return false; - if (l.minimumFrameRate() > r.minimumFrameRate()) return true; - if (l.minimumFrameRate() < r.minimumFrameRate()) return false; - if (l.maximumFrameRate() > r.maximumFrameRate()) return true; - if (l.maximumFrameRate() < r.maximumFrameRate()) return false; + if (l.minFrameRate() > r.minFrameRate()) return true; + if (l.minFrameRate() < r.minFrameRate()) return false; + if (l.maxFrameRate() > r.maxFrameRate()) return true; + if (l.maxFrameRate() < r.maxFrameRate()) return false; if (l.pixelFormat() > r.pixelFormat()) return true; if (l.pixelFormat() < r.pixelFormat()) return false; - if (l.pixelAspectRatio().width() > r.pixelAspectRatio().width()) return true; - if (l.pixelAspectRatio().width() < r.pixelAspectRatio().width()) return false; - if (l.pixelAspectRatio().height() > r.pixelAspectRatio().height()) return true; - if (l.pixelAspectRatio().height() < r.pixelAspectRatio().height()) return false; return false; }); - for (const QCameraViewfinderSettings& setting : settings) + for (const QCameraFormat& setting : settings) { if (setting.isNull()) continue; - const QString description = tr("%0x%1, %2-%3 FPS, Format=%4, PixelAspectRatio=%5x%6") + const QString description = tr("%0x%1, %2-%3 FPS, Format=%4") .arg(setting.resolution().width()) .arg(setting.resolution().height()) - .arg(setting.minimumFrameRate()) - .arg(setting.maximumFrameRate()) - .arg(QString::fromStdString(fmt::format("%s", setting.pixelFormat()))) - .arg(setting.pixelAspectRatio().width()) - .arg(setting.pixelAspectRatio().height()); + .arg(setting.minFrameRate()) + .arg(setting.maxFrameRate()) + .arg(QString::fromStdString(fmt::format("%s", setting.pixelFormat()))); ui->combo_settings->addItem(description, QVariant::fromValue(setting)); } ui->combo_settings->blockSignals(false); @@ -181,37 +170,27 @@ void camera_settings_dialog::handle_camera_change(int index) // Load selected settings from config file int index = 0; bool success = false; - const std::string key = camera_info.deviceName().toStdString(); + const std::string key = camera_info.id().toStdString(); cfg_camera::camera_setting cfg_setting = g_cfg_camera.get_camera_setting(key, success); if (success) { camera_log.notice("Found config entry for camera \"%s\"", key); - // Convert to Qt data - QCameraViewfinderSettings setting; - setting.setResolution(cfg_setting.width, cfg_setting.height); - setting.setMinimumFrameRate(cfg_setting.min_fps); - setting.setMaximumFrameRate(cfg_setting.max_fps); - setting.setPixelFormat(static_cast(cfg_setting.format)); - setting.setPixelAspectRatio(cfg_setting.pixel_aspect_width, cfg_setting.pixel_aspect_height); - // Select matching drowdown entry const double epsilon = 0.001; for (int i = 0; i < ui->combo_settings->count(); i++) { - const QCameraViewfinderSettings tmp = ui->combo_settings->itemData(i).value(); + const QCameraFormat tmp = ui->combo_settings->itemData(i).value(); - if (tmp.resolution().width() == setting.resolution().width() && - tmp.resolution().height() == setting.resolution().height() && - tmp.minimumFrameRate() >= (setting.minimumFrameRate() - epsilon) && - tmp.minimumFrameRate() <= (setting.minimumFrameRate() + epsilon) && - tmp.maximumFrameRate() >= (setting.maximumFrameRate() - epsilon) && - tmp.maximumFrameRate() <= (setting.maximumFrameRate() + epsilon) && - tmp.pixelFormat() == setting.pixelFormat() && - tmp.pixelAspectRatio().width() == setting.pixelAspectRatio().width() && - tmp.pixelAspectRatio().height() == setting.pixelAspectRatio().height()) + if (tmp.resolution().width() == cfg_setting.width && + tmp.resolution().height() == cfg_setting.height && + tmp.minFrameRate() >= (cfg_setting.min_fps - epsilon) && + tmp.minFrameRate() <= (cfg_setting.min_fps + epsilon) && + tmp.maxFrameRate() >= (cfg_setting.max_fps - epsilon) && + tmp.maxFrameRate() <= (cfg_setting.max_fps + epsilon) && + tmp.pixelFormat() == static_cast(cfg_setting.format)) { index = i; break; @@ -223,14 +202,12 @@ void camera_settings_dialog::handle_camera_change(int index) ui->combo_settings->setEnabled(true); // Update config to match user interface outcome - const QCameraViewfinderSettings setting = ui->combo_settings->currentData().value(); + const QCameraFormat setting = ui->combo_settings->currentData().value(); cfg_setting.width = setting.resolution().width(); cfg_setting.height = setting.resolution().height(); - cfg_setting.min_fps = setting.minimumFrameRate(); - cfg_setting.max_fps = setting.maximumFrameRate(); + cfg_setting.min_fps = setting.minFrameRate(); + cfg_setting.max_fps = setting.maxFrameRate(); cfg_setting.format = static_cast(setting.pixelFormat()); - cfg_setting.pixel_aspect_width = setting.pixelAspectRatio().width(); - cfg_setting.pixel_aspect_height = setting.pixelAspectRatio().height(); g_cfg_camera.set_camera_setting(key, cfg_setting); } } @@ -248,23 +225,21 @@ void camera_settings_dialog::handle_settings_change(int index) return; } - if (index >= 0 && ui->combo_settings->itemData(index).canConvert() && ui->combo_camera->currentData().canConvert()) + if (index >= 0 && ui->combo_settings->itemData(index).canConvert() && ui->combo_camera->currentData().canConvert()) { - const QCameraViewfinderSettings setting = ui->combo_settings->itemData(index).value(); + const QCameraFormat setting = ui->combo_settings->itemData(index).value(); if (!setting.isNull()) { - m_camera->setViewfinderSettings(setting); + m_camera->setCameraFormat(setting); } cfg_camera::camera_setting cfg_setting; cfg_setting.width = setting.resolution().width(); cfg_setting.height = setting.resolution().height(); - cfg_setting.min_fps = setting.minimumFrameRate(); - cfg_setting.max_fps = setting.maximumFrameRate(); + cfg_setting.min_fps = setting.minFrameRate(); + cfg_setting.max_fps = setting.maxFrameRate(); cfg_setting.format = static_cast(setting.pixelFormat()); - cfg_setting.pixel_aspect_width = setting.pixelAspectRatio().width(); - cfg_setting.pixel_aspect_height = setting.pixelAspectRatio().height(); - g_cfg_camera.set_camera_setting(ui->combo_camera->currentData().value().deviceName().toStdString(), cfg_setting); + g_cfg_camera.set_camera_setting(ui->combo_camera->currentData().value().id().toStdString(), cfg_setting); } m_camera->start(); diff --git a/rpcs3/rpcs3qt/camera_settings_dialog.h b/rpcs3/rpcs3qt/camera_settings_dialog.h index 56aa29a6cf..da18f64cca 100644 --- a/rpcs3/rpcs3qt/camera_settings_dialog.h +++ b/rpcs3/rpcs3qt/camera_settings_dialog.h @@ -2,6 +2,7 @@ #include #include +#include namespace Ui { @@ -25,5 +26,6 @@ private: void save_config(); std::unique_ptr ui; - std::shared_ptr m_camera; + std::unique_ptr m_camera; + std::unique_ptr m_media_capture_session; }; diff --git a/rpcs3/rpcs3qt/camera_settings_dialog.ui b/rpcs3/rpcs3qt/camera_settings_dialog.ui index 6deef8173e..8afe262f22 100644 --- a/rpcs3/rpcs3qt/camera_settings_dialog.ui +++ b/rpcs3/rpcs3qt/camera_settings_dialog.ui @@ -57,7 +57,7 @@ - + 64 @@ -86,9 +86,9 @@ - QCameraViewfinder + QVideoWidget QWidget -
qcameraviewfinder.h
+
qvideowidget.h
1
diff --git a/rpcs3/rpcs3qt/config_checker.cpp b/rpcs3/rpcs3qt/config_checker.cpp index c838b8831c..3cf34955c8 100644 --- a/rpcs3/rpcs3qt/config_checker.cpp +++ b/rpcs3/rpcs3qt/config_checker.cpp @@ -64,12 +64,12 @@ bool config_checker::check_config(QString content, QString& result, bool is_log) const QString start_token = "SYS: Used configuration:\n"; const QString end_token = "\n·"; - int start = content.indexOf(start_token); - int end = -1; + qsizetype start = content.indexOf(start_token); + qsizetype end = -1; if (start >= 0) { - start += start_token.count(); + start += start_token.size(); end = content.indexOf(end_token, start); } diff --git a/rpcs3/rpcs3qt/custom_table_widget_item.cpp b/rpcs3/rpcs3qt/custom_table_widget_item.cpp index 83235e395a..d679797134 100644 --- a/rpcs3/rpcs3qt/custom_table_widget_item.cpp +++ b/rpcs3/rpcs3qt/custom_table_widget_item.cpp @@ -30,35 +30,35 @@ bool custom_table_widget_item::operator<(const QTableWidgetItem& other) const const QVariant data_l = data(m_sort_role); const QVariant data_r = other.data(m_sort_role); - const QVariant::Type type_l = data_l.type(); - const QVariant::Type type_r = data_r.type(); + const int type_l = data_l.metaType().id(); + const int type_r = data_r.metaType().id(); ensure(type_l == type_r); switch (type_l) { - case QVariant::Type::Bool: - case QVariant::Type::Int: + case QMetaType::Type::Bool: + case QMetaType::Type::Int: return data_l.toInt() < data_r.toInt(); - case QVariant::Type::UInt: + case QMetaType::Type::UInt: return data_l.toUInt() < data_r.toUInt(); - case QVariant::Type::LongLong: + case QMetaType::Type::LongLong: return data_l.toLongLong() < data_r.toLongLong(); - case QVariant::Type::ULongLong: + case QMetaType::Type::ULongLong: return data_l.toULongLong() < data_r.toULongLong(); - case QVariant::Type::Double: + case QMetaType::Type::Double: return data_l.toDouble() < data_r.toDouble(); - case QVariant::Type::Date: + case QMetaType::Type::QDate: return data_l.toDate() < data_r.toDate(); - case QVariant::Type::Time: + case QMetaType::Type::QTime: return data_l.toTime() < data_r.toTime(); - case QVariant::Type::DateTime: + case QMetaType::Type::QDateTime: return data_l.toDateTime() < data_r.toDateTime(); - case QVariant::Type::Char: - case QVariant::Type::String: + case QMetaType::Type::Char: + case QMetaType::Type::QString: return data_l.toString() < data_r.toString(); default: - fmt::throw_exception("Unimplemented type %s", QVariant::typeToName(type_l)); + fmt::throw_exception("Unimplemented type %s", QMetaType(type_l).name()); } } diff --git a/rpcs3/rpcs3qt/gs_frame.h b/rpcs3/rpcs3qt/gs_frame.h index feaaf88f23..b39c790e01 100644 --- a/rpcs3/rpcs3qt/gs_frame.h +++ b/rpcs3/rpcs3qt/gs_frame.h @@ -73,7 +73,7 @@ public: void take_screenshot(std::vector data, const u32 sshot_width, const u32 sshot_height, bool is_bgra) override; protected: - virtual void paintEvent(QPaintEvent *event); + void paintEvent(QPaintEvent *event) override; void showEvent(QShowEvent *event) override; void keyPressEvent(QKeyEvent *keyEvent) override; diff --git a/rpcs3/rpcs3qt/gui_application.cpp b/rpcs3/rpcs3qt/gui_application.cpp index a36efa071a..244a9fa6aa 100644 --- a/rpcs3/rpcs3qt/gui_application.cpp +++ b/rpcs3/rpcs3qt/gui_application.cpp @@ -34,7 +34,6 @@ #include #include #include -#include #include #include @@ -173,7 +172,7 @@ void gui_application::SwitchTranslator(QTranslator& translator, const QString& f // remove the old translator removeTranslator(&translator); - const QString lang_path = QLibraryInfo::location(QLibraryInfo::TranslationsPath) + QStringLiteral("/"); + const QString lang_path = QLibraryInfo::path(QLibraryInfo::TranslationsPath) + QStringLiteral("/"); const QString file_path = lang_path + filename; if (QFileInfo(file_path).isFile()) @@ -236,7 +235,7 @@ QStringList gui_application::GetAvailableLanguageCodes() { QStringList language_codes; - const QString language_path = QLibraryInfo::location(QLibraryInfo::TranslationsPath); + const QString language_path = QLibraryInfo::path(QLibraryInfo::TranslationsPath); if (QFileInfo(language_path).isDir()) { @@ -546,13 +545,17 @@ void gui_application::InitializeCallbacks() return localized_emu::get_u32string(id, args); }; - callbacks.play_sound = [](const std::string& path) + callbacks.play_sound = [this](const std::string& path) { - Emu.CallFromMainThread([path]() + Emu.CallFromMainThread([this, path]() { if (fs::is_file(path)) { - QSound::play(qstr(path)); + m_sound_effect.stop(); + m_sound_effect.setSource(QUrl::fromLocalFile(qstr(path))); + m_sound_effect.setVolume(g_cfg.audio.volume * 0.01f); + m_sound_effect.setLoopCount(1); + m_sound_effect.play(); } }); }; diff --git a/rpcs3/rpcs3qt/gui_application.h b/rpcs3/rpcs3qt/gui_application.h index ff86ea56d3..d6fdb6bc9c 100644 --- a/rpcs3/rpcs3qt/gui_application.h +++ b/rpcs3/rpcs3qt/gui_application.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "main_application.h" @@ -87,6 +88,8 @@ private: QTimer m_timer; QElapsedTimer m_timer_playtime; + QSoundEffect m_sound_effect{}; + std::shared_ptr m_emu_settings; std::shared_ptr m_gui_settings; std::shared_ptr m_persistent_settings; diff --git a/rpcs3/rpcs3qt/log_viewer.cpp b/rpcs3/rpcs3qt/log_viewer.cpp index 47c78e40b2..d39bfd5367 100644 --- a/rpcs3/rpcs3qt/log_viewer.cpp +++ b/rpcs3/rpcs3qt/log_viewer.cpp @@ -235,8 +235,11 @@ void log_viewer::show_log() { m_gui_settings->SetValue(gui::fd_log_viewer, m_path_last); - QTextStream stream(&file); - m_full_log = stream.readAll(); + // TODO: Due to a bug in Qt 6.5.2 QTextStream::readAll is ridiculously slow to the point where it gets stuck on large files. + // In Qt 5.15.2 this was much faster than QFile::readAll. Use QTextStream again once this bug is fixed upstream. + //QTextStream stream(&file); + //m_full_log = stream.readAll(); + m_full_log = file.readAll(); m_full_log.replace('\0', '0'); file.close(); } diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 25f7369a83..1aca508ce2 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -155,7 +155,7 @@ bool main_window::Init([[maybe_unused]] bool with_cli_boot) ui->toolbar_start->setEnabled(enable_play_last); // create tool buttons for the taskbar thumbnail -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_thumb_bar = new QWinThumbnailToolBar(this); m_thumb_bar->setWindow(windowHandle()); @@ -1700,7 +1700,7 @@ void main_window::RepaintThumbnailIcons() return gui::utils::get_colorized_icon(QPixmap::fromImage(gui::utils::get_opaque_image_area(path)), Qt::black, new_color); }; -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF if (!m_thumb_bar) return; m_icon_thumb_play = icon(":/Icons/play.png"); @@ -1787,14 +1787,6 @@ void main_window::RepaintToolBarIcons() // resize toolbar elements - // for highdpi resize toolbar icons and height dynamically - // choose factors to mimic Gui-Design in main_window.ui - // TODO: delete this in case Qt::AA_EnableHighDpiScaling is enabled in main.cpp -#ifdef _WIN32 - const int tool_icon_height = menuBar()->sizeHint().height() * 1.5; - ui->toolBar->setIconSize(QSize(tool_icon_height, tool_icon_height)); -#endif - const int tool_bar_height = ui->toolBar->sizeHint().height(); for (const auto& act : ui->toolBar->actions()) @@ -1820,7 +1812,7 @@ void main_window::OnEmuRun(bool /*start_playtime*/) const m_debugger_frame->EnableButtons(true); -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_thumb_stop->setToolTip(stop_tooltip); m_thumb_restart->setToolTip(restart_tooltip); m_thumb_playPause->setToolTip(pause_tooltip); @@ -1843,7 +1835,7 @@ void main_window::OnEmuResume() const const QString pause_tooltip = tr("Pause %0").arg(title); const QString stop_tooltip = tr("Stop %0").arg(title); -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_thumb_stop->setToolTip(stop_tooltip); m_thumb_restart->setToolTip(restart_tooltip); m_thumb_playPause->setToolTip(pause_tooltip); @@ -1862,7 +1854,7 @@ void main_window::OnEmuPause() const const QString title = GetCurrentTitle(); const QString resume_tooltip = tr("Resume %0").arg(title); -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_thumb_playPause->setToolTip(resume_tooltip); m_thumb_playPause->setIcon(m_icon_thumb_play); #endif @@ -1886,7 +1878,7 @@ void main_window::OnEmuStop() ui->sysPauseAct->setText(tr("&Play")); ui->sysPauseAct->setIcon(m_icon_play); -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_thumb_playPause->setToolTip(play_tooltip); m_thumb_playPause->setIcon(m_icon_thumb_play); #endif @@ -1908,7 +1900,7 @@ void main_window::OnEmuStop() ui->toolbar_start->setText(tr("Restart")); ui->toolbar_start->setToolTip(restart_tooltip); ui->sysRebootAct->setEnabled(true); -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_thumb_restart->setToolTip(restart_tooltip); m_thumb_restart->setEnabled(true); #endif @@ -1947,7 +1939,7 @@ void main_window::OnEmuReady() const const QString play_tooltip = tr("Play %0").arg(title); m_debugger_frame->EnableButtons(true); -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_thumb_playPause->setToolTip(play_tooltip); m_thumb_playPause->setIcon(m_icon_thumb_play); #endif @@ -1971,7 +1963,7 @@ void main_window::OnEmuReady() const void main_window::EnableMenus(bool enabled) const { // Thumbnail Buttons -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_thumb_playPause->setEnabled(enabled); m_thumb_stop->setEnabled(enabled); m_thumb_restart->setEnabled(enabled); @@ -3139,14 +3131,14 @@ void main_window::CreateDockWindows() ui->toolbar_start->setEnabled(enable_play_buttons); ui->sysPauseAct->setEnabled(enable_play_buttons); -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_thumb_playPause->setEnabled(enable_play_buttons); #endif if (!tooltip.isEmpty()) { ui->toolbar_start->setToolTip(tooltip); -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_thumb_playPause->setToolTip(tooltip); #endif } diff --git a/rpcs3/rpcs3qt/main_window.h b/rpcs3/rpcs3qt/main_window.h index 17ec7ea30e..a776e60412 100644 --- a/rpcs3/rpcs3qt/main_window.h +++ b/rpcs3/rpcs3qt/main_window.h @@ -1,6 +1,6 @@ #pragma once -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF #include #include #endif @@ -60,7 +60,7 @@ class main_window : public QMainWindow QIcon m_icon_fullscreen_on; QIcon m_icon_fullscreen_off; -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF QIcon m_icon_thumb_play; QIcon m_icon_thumb_pause; QIcon m_icon_thumb_stop; diff --git a/rpcs3/rpcs3qt/memory_viewer_panel.cpp b/rpcs3/rpcs3qt/memory_viewer_panel.cpp index f8ee6fcebe..19200c8249 100644 --- a/rpcs3/rpcs3qt/memory_viewer_panel.cpp +++ b/rpcs3/rpcs3qt/memory_viewer_panel.cpp @@ -1150,7 +1150,7 @@ void memory_viewer_panel::ShowImage(QWidget* parent, u32 addr, color_format form { if (object == m_canvas && (event->type() == QEvent::HoverMove || event->type() == QEvent::HoverEnter || event->type() == QEvent::HoverLeave)) { - const QPoint xy = static_cast(event)->pos() / m_canvas_scale; + const QPointF xy = static_cast(event)->position() / m_canvas_scale; set_window_name_by_coordinates(xy.x(), xy.y()); return false; } @@ -1159,7 +1159,7 @@ void memory_viewer_panel::ShowImage(QWidget* parent, u32 addr, color_format form { QLineEdit* addr_line = static_cast(parent())->m_addr_line; - const QPoint xy = static_cast(event)->pos() / m_canvas_scale; + const QPointF xy = static_cast(event)->position() / m_canvas_scale; addr_line->setText(qstr(fmt::format("%08x", get_pointed_addr(xy.x(), xy.y())))); Q_EMIT addr_line->returnPressed(); close(); diff --git a/rpcs3/rpcs3qt/osk_dialog_frame.cpp b/rpcs3/rpcs3qt/osk_dialog_frame.cpp index 031199789a..32336f44c5 100644 --- a/rpcs3/rpcs3qt/osk_dialog_frame.cpp +++ b/rpcs3/rpcs3qt/osk_dialog_frame.cpp @@ -182,7 +182,7 @@ void osk_dialog_frame::Create(const osk_params& params) void osk_dialog_frame::SetOskText(const QString& text) { - std::memcpy(osk_text.data(), utils::bless(text.constData()), std::min(osk_text.size(), text.size() + usz{1}) * sizeof(char16_t)); + std::memcpy(osk_text.data(), utils::bless(text.constData()), std::min(osk_text.size(), text.size() + usz{1}) * sizeof(char16_t)); } void osk_dialog_frame::Close(s32 status) diff --git a/rpcs3/rpcs3qt/progress_indicator.cpp b/rpcs3/rpcs3qt/progress_indicator.cpp index 335b5b0985..3130444fbf 100644 --- a/rpcs3/rpcs3qt/progress_indicator.cpp +++ b/rpcs3/rpcs3qt/progress_indicator.cpp @@ -1,6 +1,6 @@ #include "progress_indicator.h" -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF #include #include #elif HAVE_QTDBUS @@ -10,7 +10,7 @@ progress_indicator::progress_indicator(int minimum, int maximum) { -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_tb_button = std::make_unique(); m_tb_button->progress()->setRange(minimum, maximum); m_tb_button->progress()->setVisible(false); @@ -25,7 +25,7 @@ progress_indicator::progress_indicator(int minimum, int maximum) progress_indicator::~progress_indicator() { -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF // QWinTaskbarProgress::hide() will crash if the application is already about to close, even if the object is not null. if (!QCoreApplication::closingDown()) { @@ -38,7 +38,7 @@ progress_indicator::~progress_indicator() void progress_indicator::show(QWindow* window) { -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_tb_button->setWindow(window); m_tb_button->progress()->show(); #else @@ -48,7 +48,7 @@ void progress_indicator::show(QWindow* window) int progress_indicator::value() const { -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF return m_tb_button->progress()->value(); #else return m_value; @@ -57,7 +57,7 @@ int progress_indicator::value() const void progress_indicator::set_value(int value) { -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_tb_button->progress()->setValue(std::clamp(value, m_tb_button->progress()->minimum(), m_tb_button->progress()->maximum())); #else m_value = std::clamp(value, m_minimum, m_maximum); @@ -69,7 +69,7 @@ void progress_indicator::set_value(int value) void progress_indicator::set_range(int minimum, int maximum) { -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_tb_button->progress()->setRange(minimum, maximum); #else m_minimum = minimum; @@ -79,7 +79,7 @@ void progress_indicator::set_range(int minimum, int maximum) void progress_indicator::reset() { -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_tb_button->progress()->reset(); #else m_value = m_minimum; @@ -91,7 +91,7 @@ void progress_indicator::reset() void progress_indicator::signal_failure() { -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_tb_button->progress()->stop(); #elif HAVE_QTDBUS update_progress(0, false, true); diff --git a/rpcs3/rpcs3qt/progress_indicator.h b/rpcs3/rpcs3qt/progress_indicator.h index 1eccced1db..9e27f40ab4 100644 --- a/rpcs3/rpcs3qt/progress_indicator.h +++ b/rpcs3/rpcs3qt/progress_indicator.h @@ -2,7 +2,7 @@ #include -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF #include #endif @@ -23,7 +23,7 @@ public: private: -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF std::unique_ptr m_tb_button; #else int m_value = 0; diff --git a/rpcs3/rpcs3qt/qt_camera_error_handler.cpp b/rpcs3/rpcs3qt/qt_camera_error_handler.cpp index 9a05816268..f7bec4fee8 100644 --- a/rpcs3/rpcs3qt/qt_camera_error_handler.cpp +++ b/rpcs3/rpcs3qt/qt_camera_error_handler.cpp @@ -3,40 +3,14 @@ LOG_CHANNEL(camera_log, "Camera"); -template <> -void fmt_class_string::format(std::string& out, u64 arg) -{ - format_enum(out, arg, [](QCamera::Status value) - { - switch (value) - { - case QCamera::Status::UnavailableStatus: return "Unavailable"; - case QCamera::Status::UnloadedStatus: return "Unloaded"; - case QCamera::Status::LoadingStatus: return "Loading"; - case QCamera::Status::UnloadingStatus: return "Unloading"; - case QCamera::Status::LoadedStatus: return "Loaded"; - case QCamera::Status::StandbyStatus: return "Standby"; - case QCamera::Status::StartingStatus: return "Starting"; - case QCamera::Status::StoppingStatus: return "Stopping"; - case QCamera::Status::ActiveStatus: return "Active"; - } - - return unknown; - }); -} - -qt_camera_error_handler::qt_camera_error_handler(std::shared_ptr camera, std::function status_callback) +qt_camera_error_handler::qt_camera_error_handler(std::shared_ptr camera, std::function status_callback) : m_camera(std::move(camera)) , m_status_callback(std::move(status_callback)) { if (m_camera) { - connect(m_camera.get(), QOverload::of(&QCamera::availabilityChanged), this, &qt_camera_error_handler::handle_availability); - connect(m_camera.get(), &QCamera::stateChanged, this, &qt_camera_error_handler::handle_camera_state); - connect(m_camera.get(), &QCamera::statusChanged, this, &qt_camera_error_handler::handle_camera_status); + connect(m_camera.get(), &QCamera::activeChanged, this, &qt_camera_error_handler::handle_camera_active); connect(m_camera.get(), &QCamera::errorOccurred, this, &qt_camera_error_handler::handle_camera_error); - connect(m_camera.get(), &QCamera::captureModeChanged, this, &qt_camera_error_handler::handle_capture_modes); - connect(m_camera.get(), QOverload::of(&QCamera::lockStatusChanged), this, &qt_camera_error_handler::handle_lock_status); } } @@ -44,37 +18,17 @@ qt_camera_error_handler::~qt_camera_error_handler() { } -void qt_camera_error_handler::handle_availability(QMultimedia::AvailabilityStatus availability) +void qt_camera_error_handler::handle_camera_active(bool is_active) { - camera_log.notice("Camera availability changed to %d", static_cast(availability)); -} - -void qt_camera_error_handler::handle_camera_state(QCamera::State state) -{ - camera_log.notice("Camera state changed to %d", static_cast(state)); -} - -void qt_camera_error_handler::handle_camera_status(QCamera::Status status) -{ - camera_log.notice("Camera status changed to %s", status); + camera_log.notice("Camera active status changed to %d", is_active); if (m_status_callback) { - m_status_callback(status); + m_status_callback(is_active); } } -void qt_camera_error_handler::handle_lock_status(QCamera::LockStatus status, QCamera::LockChangeReason reason) +void qt_camera_error_handler::handle_camera_error(QCamera::Error error, const QString& errorString) { - camera_log.notice("Camera lock status changed to %d (reason=%d)", static_cast(status), static_cast(reason)); -} - -void qt_camera_error_handler::handle_capture_modes(QCamera::CaptureModes capture_modes) -{ - camera_log.notice("Camera capture modes changed to %d", static_cast(capture_modes)); -} - -void qt_camera_error_handler::handle_camera_error(QCamera::Error error) -{ - camera_log.error("Error event: \"%s\" (error=%d)", m_camera ? m_camera->errorString() : "", static_cast(error)); + camera_log.error("Error event: \"%s\" (error=%d)", errorString, static_cast(error)); } diff --git a/rpcs3/rpcs3qt/qt_camera_error_handler.h b/rpcs3/rpcs3qt/qt_camera_error_handler.h index cf97a120be..a2d2c81bac 100644 --- a/rpcs3/rpcs3qt/qt_camera_error_handler.h +++ b/rpcs3/rpcs3qt/qt_camera_error_handler.h @@ -8,18 +8,14 @@ class qt_camera_error_handler : public QObject Q_OBJECT public: - qt_camera_error_handler(std::shared_ptr camera, std::function status_callback); + qt_camera_error_handler(std::shared_ptr camera, std::function status_callback); virtual ~qt_camera_error_handler(); private Q_SLOTS: - void handle_availability(QMultimedia::AvailabilityStatus availability); - void handle_lock_status(QCamera::LockStatus, QCamera::LockChangeReason); - void handle_capture_modes(QCamera::CaptureModes capture_modes); - void handle_camera_state(QCamera::State state); - void handle_camera_status(QCamera::Status status); - void handle_camera_error(QCamera::Error error); + void handle_camera_active(bool is_active); + void handle_camera_error(QCamera::Error error, const QString& errorString); private: std::shared_ptr m_camera; - std::function m_status_callback = nullptr; + std::function m_status_callback = nullptr; }; diff --git a/rpcs3/rpcs3qt/qt_camera_handler.cpp b/rpcs3/rpcs3qt/qt_camera_handler.cpp index 8a7d0c55ea..500f9eb22d 100644 --- a/rpcs3/rpcs3qt/qt_camera_handler.cpp +++ b/rpcs3/rpcs3qt/qt_camera_handler.cpp @@ -5,17 +5,16 @@ #include "Emu/Io/camera_config.h" #include "Emu/Cell/lv2/sys_event.h" -#include -#include +#include LOG_CHANNEL(camera_log, "Camera"); qt_camera_handler::qt_camera_handler() : camera_handler_base() { // List available cameras - for (const QCameraInfo& cameraInfo : QCameraInfo::availableCameras()) + for (const QCameraDevice& camera_device : QMediaDevices::videoInputs()) { - camera_log.success("Found camera: name=%s, description=%s", cameraInfo.deviceName(), cameraInfo.description()); + camera_log.success("Found camera: id=%s, description=%s", camera_device.id().toStdString(), camera_device.description()); } if (!g_cfg_camera.load()) @@ -29,60 +28,53 @@ qt_camera_handler::~qt_camera_handler() Emu.BlockingCallFromMainThread([&]() { close_camera(); - m_surface.reset(); - m_camera.reset(); - m_error_handler.reset(); + reset(); }); } -void qt_camera_handler::set_camera(const QCameraInfo& camera_info) +void qt_camera_handler::reset() +{ + m_camera.reset(); + m_error_handler.reset(); + m_video_sink.reset(); + m_media_capture_session.reset(); +} + +void qt_camera_handler::set_camera(const QCameraDevice& camera_info) { if (camera_info.isNull()) { - m_surface.reset(); - m_camera.reset(); - m_error_handler.reset(); + reset(); return; } // Determine if the camera is front facing, in which case we will need to flip the image horizontally. - const bool front_facing = camera_info.position() == QCamera::Position::FrontFace; + const bool front_facing = camera_info.position() == QCameraDevice::Position::FrontFace; - camera_log.success("Using camera: name=\"%s\", description=\"%s\", front_facing=%d", camera_info.deviceName(), camera_info.description(), front_facing); + camera_log.success("Using camera: id=\"%s\", description=\"%s\", front_facing=%d", camera_info.id().toStdString(), camera_info.description(), front_facing); // Create camera and video surface - m_surface.reset(new qt_camera_video_surface(front_facing, nullptr)); + m_media_capture_session.reset(new QMediaCaptureSession(nullptr)); + m_video_sink.reset(new qt_camera_video_sink(front_facing, nullptr)); m_camera.reset(new QCamera(camera_info)); m_error_handler.reset(new qt_camera_error_handler(m_camera, - [this](QCamera::Status status) + [this](bool is_active) { - switch (status) + if (is_active) { - case QCamera::UnavailableStatus: - m_state = camera_handler_state::not_available; - break; - case QCamera::UnloadedStatus: - case QCamera::UnloadingStatus: - m_state = camera_handler_state::closed; - break; - case QCamera::StandbyStatus: - case QCamera::StoppingStatus: - case QCamera::LoadedStatus: - case QCamera::LoadingStatus: - m_state = camera_handler_state::open; - break; - case QCamera::StartingStatus: - case QCamera::ActiveStatus: m_state = camera_handler_state::running; - break; - default: - camera_log.error("Ignoring unknown status %d", static_cast(status)); - break; + } + else + { + m_state = camera_handler_state::closed; } })); - // Set view finder and update the settings - m_camera->setViewfinder(m_surface.get()); + // Setup video sink + m_media_capture_session->setCamera(m_camera.get()); + m_media_capture_session->setVideoSink(m_video_sink.get()); + + // Update the settings update_camera_settings(); } @@ -94,25 +86,25 @@ void qt_camera_handler::open_camera() m_camera_id != camera_id) { camera_log.notice("Switching camera from %s to %s", m_camera_id, camera_id); - camera_log.notice("Unloading old camera..."); - if (m_camera) m_camera->unload(); + camera_log.notice("Stopping old camera..."); + if (m_camera) m_camera->stop(); m_camera_id = camera_id; } - QCameraInfo selected_camera; + QCameraDevice selected_camera{}; if (m_camera_id == g_cfg.io.camera_id.def) { - selected_camera = QCameraInfo::defaultCamera(); + selected_camera = QMediaDevices::defaultVideoInput(); } else if (!m_camera_id.empty()) { const QString camera_id = QString::fromStdString(m_camera_id); - for (const QCameraInfo& camera_info : QCameraInfo::availableCameras()) + for (const QCameraDevice& camera_device : QMediaDevices::videoInputs()) { - if (camera_id == camera_info.deviceName()) + if (camera_id == camera_device.id()) { - selected_camera = camera_info; + selected_camera = camera_device; break; } } @@ -124,35 +116,26 @@ void qt_camera_handler::open_camera() { if (m_camera_id.empty()) camera_log.notice("Camera disabled"); else camera_log.error("No camera found"); - m_state = camera_handler_state::not_available; + m_state = camera_handler_state::closed; return; } - if (m_camera->state() != QCamera::State::UnloadedState) + if (m_camera->isActive()) { - camera_log.notice("Camera already loaded"); + camera_log.notice("Camera already active"); return; } - // Load/open camera - m_camera->load(); - // List all supported formats for debugging - for (const QCamera::FrameRateRange& frame_rate : m_camera->supportedViewfinderFrameRateRanges()) + for (const QCameraFormat& format : m_camera->cameraDevice().videoFormats()) { - camera_log.notice("Supported frame rate range: %f-%f", frame_rate.minimumFrameRate, frame_rate.maximumFrameRate); - } - for (const QVideoFrame::PixelFormat& pixel_format : m_camera->supportedViewfinderPixelFormats()) - { - camera_log.notice("Supported pixel format: %d", static_cast(pixel_format)); - } - for (const QSize& resolution : m_camera->supportedViewfinderResolutions()) - { - camera_log.notice("Supported resolution: %dx%d", resolution.width(), resolution.height()); + camera_log.notice("Supported format: pixelformat=%s, resolution=%dx%d framerate=%f-%f", format.pixelFormat(), format.resolution().width(), format.resolution().height(), format.minFrameRate(), format.maxFrameRate()); } // Update camera and view finder settings update_camera_settings(); + + m_state = camera_handler_state::open; } void qt_camera_handler::close_camera() @@ -163,18 +146,12 @@ void qt_camera_handler::close_camera() { if (m_camera_id.empty()) camera_log.notice("Camera disabled"); else camera_log.error("No camera found"); - m_state = camera_handler_state::not_available; - return; - } - - if (m_camera->state() == QCamera::State::UnloadedState) - { - camera_log.notice("Camera already unloaded"); + m_state = camera_handler_state::closed; return; } // Unload/close camera - m_camera->unload(); + m_camera->stop(); } void qt_camera_handler::start_camera() @@ -185,22 +162,16 @@ void qt_camera_handler::start_camera() { if (m_camera_id.empty()) camera_log.notice("Camera disabled"); else camera_log.error("No camera found"); - m_state = camera_handler_state::not_available; + m_state = camera_handler_state::closed; return; } - if (m_camera->state() == QCamera::State::ActiveState) + if (m_camera->isActive()) { camera_log.notice("Camera already started"); return; } - if (m_camera->state() == QCamera::State::UnloadedState) - { - camera_log.notice("Camera not open"); - open_camera(); - } - // Start camera. We will start receiving frames now. m_camera->start(); } @@ -213,11 +184,11 @@ void qt_camera_handler::stop_camera() { if (m_camera_id.empty()) camera_log.notice("Camera disabled"); else camera_log.error("No camera found"); - m_state = camera_handler_state::not_available; + m_state = camera_handler_state::closed; return; } - if (m_camera->state() == QCamera::State::LoadedState) + if (!m_camera->isActive()) { camera_log.notice("Camera already stopped"); return; @@ -232,9 +203,9 @@ void qt_camera_handler::set_format(s32 format, u32 bytesize) m_format = format; m_bytesize = bytesize; - if (m_surface) + if (m_video_sink) { - m_surface->set_format(m_format, m_bytesize); + m_video_sink->set_format(m_format, m_bytesize); } } @@ -248,9 +219,9 @@ void qt_camera_handler::set_resolution(u32 width, u32 height) m_width = width; m_height = height; - if (m_surface) + if (m_video_sink) { - m_surface->set_resolution(m_width, m_height); + m_video_sink->set_resolution(m_width, m_height); } } @@ -258,15 +229,15 @@ void qt_camera_handler::set_mirrored(bool mirrored) { m_mirrored = mirrored; - if (m_surface) + if (m_video_sink) { - m_surface->set_mirrored(m_mirrored); + m_video_sink->set_mirrored(m_mirrored); } } u64 qt_camera_handler::frame_number() const { - return m_surface ? m_surface->frame_number() : 0; + return m_video_sink ? m_video_sink->frame_number() : 0; } camera_handler_base::camera_handler_state qt_camera_handler::get_image(u8* buf, u64 size, u32& width, u32& height, u64& frame_number, u64& bytes_read) @@ -280,22 +251,22 @@ camera_handler_base::camera_handler_state qt_camera_handler::get_image(u8* buf, m_camera_id != camera_id) { camera_log.notice("Switching cameras"); - m_state = camera_handler_state::not_available; - return camera_handler_state::not_available; + m_state = camera_handler_state::closed; + return camera_handler_state::closed; } if (m_camera_id.empty()) { camera_log.notice("Camera disabled"); - m_state = camera_handler_state::not_available; - return camera_handler_state::not_available; + m_state = camera_handler_state::closed; + return camera_handler_state::closed; } - if (!m_camera || !m_surface) + if (!m_camera || !m_video_sink) { camera_log.fatal("Error: camera invalid"); - m_state = camera_handler_state::not_available; - return camera_handler_state::not_available; + m_state = camera_handler_state::closed; + return camera_handler_state::closed; } // Backup current state. State may change through events. @@ -304,7 +275,7 @@ camera_handler_base::camera_handler_state qt_camera_handler::get_image(u8* buf, if (current_state == camera_handler_state::running) { // Copy latest image into out buffer. - m_surface->get_image(buf, size, width, height, frame_number, bytes_read); + m_video_sink->get_image(buf, size, width, height, frame_number, bytes_read); } else { @@ -317,7 +288,7 @@ camera_handler_base::camera_handler_state qt_camera_handler::get_image(u8* buf, void qt_camera_handler::update_camera_settings() { // Update camera if possible. We can only do this if it is already loaded. - if (m_camera && m_camera->state() != QCamera::State::UnloadedState) + if (m_camera && m_camera->isAvailable()) { // Load selected settings from config file bool success = false; @@ -327,32 +298,23 @@ void qt_camera_handler::update_camera_settings() { camera_log.notice("Found config entry for camera \"%s\"", m_camera_id); - QCameraViewfinderSettings setting; - setting.setResolution(cfg_setting.width, cfg_setting.height); - setting.setMinimumFrameRate(cfg_setting.min_fps); - setting.setMaximumFrameRate(cfg_setting.max_fps); - setting.setPixelFormat(static_cast(cfg_setting.format)); - setting.setPixelAspectRatio(cfg_setting.pixel_aspect_width, cfg_setting.pixel_aspect_height); - // List all available settings and choose the proper value if possible. const double epsilon = 0.001; success = false; - for (const QCameraViewfinderSettings& supported_setting : m_camera->supportedViewfinderSettings(setting)) + for (const QCameraFormat& supported_setting : m_camera->cameraDevice().videoFormats()) { - if (supported_setting.resolution().width() == setting.resolution().width() && - supported_setting.resolution().height() == setting.resolution().height() && - supported_setting.minimumFrameRate() >= (setting.minimumFrameRate() - epsilon) && - supported_setting.minimumFrameRate() <= (setting.minimumFrameRate() + epsilon) && - supported_setting.maximumFrameRate() >= (setting.maximumFrameRate() - epsilon) && - supported_setting.maximumFrameRate() <= (setting.maximumFrameRate() + epsilon) && - supported_setting.pixelFormat() == setting.pixelFormat() && - supported_setting.pixelAspectRatio().width() == setting.pixelAspectRatio().width() && - supported_setting.pixelAspectRatio().height() == setting.pixelAspectRatio().height()) + if (supported_setting.resolution().width() == cfg_setting.width && + supported_setting.resolution().height() == cfg_setting.height && + supported_setting.minFrameRate() >= (cfg_setting.min_fps - epsilon) && + supported_setting.minFrameRate() <= (cfg_setting.min_fps + epsilon) && + supported_setting.maxFrameRate() >= (cfg_setting.max_fps - epsilon) && + supported_setting.maxFrameRate() <= (cfg_setting.max_fps + epsilon) && + supported_setting.pixelFormat() == static_cast(cfg_setting.format)) { // Apply settings. camera_log.notice("Setting view finder settings: frame_rate=%f, width=%d, height=%d, pixel_format=%s", - supported_setting.maximumFrameRate(), supported_setting.resolution().width(), supported_setting.resolution().height(), supported_setting.pixelFormat()); - m_camera->setViewfinderSettings(supported_setting); + supported_setting.maxFrameRate(), supported_setting.resolution().width(), supported_setting.resolution().height(), supported_setting.pixelFormat()); + m_camera->setCameraFormat(supported_setting); success = true; break; } @@ -372,10 +334,10 @@ void qt_camera_handler::update_camera_settings() } // Update video surface if possible - if (m_surface) + if (m_video_sink) { - m_surface->set_resolution(m_width, m_height); - m_surface->set_format(m_format, m_bytesize); - m_surface->set_mirrored(m_mirrored); + m_video_sink->set_resolution(m_width, m_height); + m_video_sink->set_format(m_format, m_bytesize); + m_video_sink->set_mirrored(m_mirrored); } } diff --git a/rpcs3/rpcs3qt/qt_camera_handler.h b/rpcs3/rpcs3qt/qt_camera_handler.h index 0b4f707354..4cf1f01a7e 100644 --- a/rpcs3/rpcs3qt/qt_camera_handler.h +++ b/rpcs3/rpcs3qt/qt_camera_handler.h @@ -1,12 +1,12 @@ #pragma once #include "Emu/Io/camera_handler_base.h" -#include "qt_camera_video_surface.h" +#include "qt_camera_video_sink.h" #include "qt_camera_error_handler.h" #include -#include -#include +#include +#include class qt_camera_handler final : public camera_handler_base { @@ -14,7 +14,7 @@ public: qt_camera_handler(); virtual ~qt_camera_handler(); - void set_camera(const QCameraInfo& camera_info); + void set_camera(const QCameraDevice& camera_info); void open_camera() override; void close_camera() override; @@ -28,10 +28,12 @@ public: camera_handler_state get_image(u8* buf, u64 size, u32& width, u32& height, u64& frame_number, u64& bytes_read) override; private: + void reset(); void update_camera_settings(); std::string m_camera_id; std::shared_ptr m_camera; - std::unique_ptr m_surface; + std::unique_ptr m_media_capture_session; + std::unique_ptr m_video_sink; std::unique_ptr m_error_handler; }; diff --git a/rpcs3/rpcs3qt/qt_camera_video_surface.cpp b/rpcs3/rpcs3qt/qt_camera_video_sink.cpp similarity index 78% rename from rpcs3/rpcs3qt/qt_camera_video_surface.cpp rename to rpcs3/rpcs3qt/qt_camera_video_sink.cpp index 459c632d0d..4f19155be9 100644 --- a/rpcs3/rpcs3qt/qt_camera_video_surface.cpp +++ b/rpcs3/rpcs3qt/qt_camera_video_sink.cpp @@ -1,5 +1,5 @@ #include "stdafx.h" -#include "qt_camera_video_surface.h" +#include "qt_camera_video_sink.h" #include "Emu/Cell/Modules/cellCamera.h" #include "Emu/system_config.h" @@ -8,12 +8,13 @@ LOG_CHANNEL(camera_log, "Camera"); -qt_camera_video_surface::qt_camera_video_surface(bool front_facing, QObject *parent) - : QAbstractVideoSurface(parent), m_front_facing(front_facing) +qt_camera_video_sink::qt_camera_video_sink(bool front_facing, QObject *parent) + : QVideoSink(parent), m_front_facing(front_facing) { + connect(this, &QVideoSink::videoFrameChanged, this, &qt_camera_video_sink::present); } -qt_camera_video_surface::~qt_camera_video_surface() +qt_camera_video_sink::~qt_camera_video_sink() { std::lock_guard lock(m_mutex); @@ -28,51 +29,7 @@ qt_camera_video_surface::~qt_camera_video_surface() } } -QList qt_camera_video_surface::supportedPixelFormats(QAbstractVideoBuffer::HandleType type) const -{ - Q_UNUSED(type) - - // Support all cameras - QList result; - result - << QVideoFrame::Format_ARGB32 - << QVideoFrame::Format_ARGB32_Premultiplied - << QVideoFrame::Format_RGB32 - << QVideoFrame::Format_RGB24 - << QVideoFrame::Format_RGB565 - << QVideoFrame::Format_RGB555 - << QVideoFrame::Format_ARGB8565_Premultiplied - << QVideoFrame::Format_BGRA32 - << QVideoFrame::Format_BGRA32_Premultiplied - << QVideoFrame::Format_BGR32 - << QVideoFrame::Format_BGR24 - << QVideoFrame::Format_BGR565 - << QVideoFrame::Format_BGR555 - << QVideoFrame::Format_BGRA5658_Premultiplied - << QVideoFrame::Format_AYUV444 - << QVideoFrame::Format_AYUV444_Premultiplied - << QVideoFrame::Format_YUV444 - << QVideoFrame::Format_YUV420P - << QVideoFrame::Format_YV12 - << QVideoFrame::Format_UYVY - << QVideoFrame::Format_YUYV - << QVideoFrame::Format_NV12 - << QVideoFrame::Format_NV21 - << QVideoFrame::Format_IMC1 - << QVideoFrame::Format_IMC2 - << QVideoFrame::Format_IMC3 - << QVideoFrame::Format_IMC4 - << QVideoFrame::Format_Y8 - << QVideoFrame::Format_Y16 - << QVideoFrame::Format_Jpeg - << QVideoFrame::Format_CameraRaw - << QVideoFrame::Format_AdobeDng - << QVideoFrame::Format_ABGR32 - << QVideoFrame::Format_YUV422P; - return result; -} - -bool qt_camera_video_surface::present(const QVideoFrame& frame) +bool qt_camera_video_sink::present(const QVideoFrame& frame) { if (!frame.isValid()) { @@ -82,18 +39,18 @@ bool qt_camera_video_surface::present(const QVideoFrame& frame) // Get video image. Map frame for faster read operations. QVideoFrame tmp(frame); - if (!tmp.map(QAbstractVideoBuffer::ReadOnly)) + if (!tmp.map(QVideoFrame::ReadOnly)) { camera_log.error("Failed to map video frame"); return false; } // Get image. This usually also converts the image to ARGB32. - QImage image = frame.image(); + QImage image = frame.toImage(); if (image.isNull()) { - camera_log.warning("Image is invalid: pixel_format=%s, format=%d", tmp.pixelFormat(), static_cast(QVideoFrame::imageFormatFromPixelFormat(tmp.pixelFormat()))); + camera_log.warning("Image is invalid: pixel_format=%s, format=%d", tmp.pixelFormat(), static_cast(QVideoFrameFormat::imageFormatFromPixelFormat(tmp.pixelFormat()))); } else { @@ -293,7 +250,7 @@ bool qt_camera_video_surface::present(const QVideoFrame& frame) return true; } -void qt_camera_video_surface::set_format(s32 format, u32 bytesize) +void qt_camera_video_sink::set_format(s32 format, u32 bytesize) { camera_log.notice("Setting format: format=%d, bytesize=%d", format, bytesize); @@ -301,7 +258,7 @@ void qt_camera_video_surface::set_format(s32 format, u32 bytesize) m_bytesize = bytesize; } -void qt_camera_video_surface::set_resolution(u32 width, u32 height) +void qt_camera_video_sink::set_resolution(u32 width, u32 height) { camera_log.notice("Setting resolution: width=%d, height=%d", width, height); @@ -309,19 +266,19 @@ void qt_camera_video_surface::set_resolution(u32 width, u32 height) m_height = height; } -void qt_camera_video_surface::set_mirrored(bool mirrored) +void qt_camera_video_sink::set_mirrored(bool mirrored) { camera_log.notice("Setting mirrored: mirrored=%d", mirrored); m_mirrored = mirrored; } -u64 qt_camera_video_surface::frame_number() const +u64 qt_camera_video_sink::frame_number() const { return m_frame_number.load(); } -void qt_camera_video_surface::get_image(u8* buf, u64 size, u32& width, u32& height, u64& frame_number, u64& bytes_read) +void qt_camera_video_sink::get_image(u8* buf, u64 size, u32& width, u32& height, u64& frame_number, u64& bytes_read) { // Lock read buffer std::lock_guard lock(m_mutex); @@ -348,7 +305,7 @@ void qt_camera_video_surface::get_image(u8* buf, u64 size, u32& width, u32& heig } } -u32 qt_camera_video_surface::read_index() const +u32 qt_camera_video_sink::read_index() const { // The read buffer index cannot be the same as the write index return (m_write_index + 1u) % ::narrow(m_image_buffer.size()); diff --git a/rpcs3/rpcs3qt/qt_camera_video_surface.h b/rpcs3/rpcs3qt/qt_camera_video_sink.h similarity index 66% rename from rpcs3/rpcs3qt/qt_camera_video_surface.h rename to rpcs3/rpcs3qt/qt_camera_video_sink.h index 4cd42a4cd5..3385a5048f 100644 --- a/rpcs3/rpcs3qt/qt_camera_video_surface.h +++ b/rpcs3/rpcs3qt/qt_camera_video_sink.h @@ -1,18 +1,18 @@ #pragma once -#include +#include +#include #include #include -class qt_camera_video_surface final : public QAbstractVideoSurface +class qt_camera_video_sink final : public QVideoSink { public: - qt_camera_video_surface(bool front_facing, QObject *parent = nullptr); - virtual ~qt_camera_video_surface(); + qt_camera_video_sink(bool front_facing, QObject *parent = nullptr); + virtual ~qt_camera_video_sink(); - QList supportedPixelFormats(QAbstractVideoBuffer::HandleType type = QAbstractVideoBuffer::NoHandle) const override; - bool present(const QVideoFrame& frame) override; + bool present(const QVideoFrame& frame); void set_format(s32 format, u32 bytesize); void set_resolution(u32 width, u32 height); diff --git a/rpcs3/rpcs3qt/qt_music_error_handler.cpp b/rpcs3/rpcs3qt/qt_music_error_handler.cpp index a75bde94b8..347e52934a 100644 --- a/rpcs3/rpcs3qt/qt_music_error_handler.cpp +++ b/rpcs3/rpcs3qt/qt_music_error_handler.cpp @@ -15,8 +15,6 @@ void fmt_class_string::format(std::string& out, u64 arg) case QMediaPlayer::Error::FormatError: return "FormatError"; case QMediaPlayer::Error::NetworkError: return "NetworkError"; case QMediaPlayer::Error::AccessDeniedError: return "AccessDeniedError"; - case QMediaPlayer::Error::ServiceMissingError: return "ServiceMissingError"; - case QMediaPlayer::Error::MediaIsPlaylist: return "MediaIsPlaylist"; } return unknown; @@ -30,7 +28,6 @@ void fmt_class_string::format(std::string& out, u64 a { switch (value) { - case QMediaPlayer::MediaStatus::UnknownMediaStatus: return "UnknownMediaStatus"; case QMediaPlayer::MediaStatus::NoMedia: return "NoMedia"; case QMediaPlayer::MediaStatus::LoadingMedia: return "LoadingMedia"; case QMediaPlayer::MediaStatus::LoadedMedia: return "LoadedMedia"; @@ -46,15 +43,15 @@ void fmt_class_string::format(std::string& out, u64 a } template <> -void fmt_class_string::format(std::string& out, u64 arg) +void fmt_class_string::format(std::string& out, u64 arg) { - format_enum(out, arg, [](QMediaPlayer::State value) + format_enum(out, arg, [](QMediaPlayer::PlaybackState value) { switch (value) { - case QMediaPlayer::State::StoppedState: return "StoppedState"; - case QMediaPlayer::State::PlayingState: return "PlayingState"; - case QMediaPlayer::State::PausedState: return "PausedState"; + case QMediaPlayer::PlaybackState::StoppedState: return "StoppedState"; + case QMediaPlayer::PlaybackState::PlayingState: return "PlayingState"; + case QMediaPlayer::PlaybackState::PausedState: return "PausedState"; } return unknown; @@ -68,8 +65,8 @@ qt_music_error_handler::qt_music_error_handler(std::shared_ptr med if (m_media_player) { connect(m_media_player.get(), &QMediaPlayer::mediaStatusChanged, this, &qt_music_error_handler::handle_media_status); - connect(m_media_player.get(), &QMediaPlayer::stateChanged, this, &qt_music_error_handler::handle_music_state); - connect(m_media_player.get(), QOverload::of(&QMediaPlayer::error), this, &qt_music_error_handler::handle_music_error); + connect(m_media_player.get(), &QMediaPlayer::playbackStateChanged, this, &qt_music_error_handler::handle_music_state); + connect(m_media_player.get(), &QMediaPlayer::errorOccurred, this, &qt_music_error_handler::handle_music_error); } } @@ -87,12 +84,12 @@ void qt_music_error_handler::handle_media_status(QMediaPlayer::MediaStatus statu } } -void qt_music_error_handler::handle_music_state(QMediaPlayer::State state) +void qt_music_error_handler::handle_music_state(QMediaPlayer::PlaybackState state) { music_log.notice("New playback state: %s (state=%d)", state, static_cast(state)); } -void qt_music_error_handler::handle_music_error(QMediaPlayer::Error error) +void qt_music_error_handler::handle_music_error(QMediaPlayer::Error error, const QString& errorString) { - music_log.error("Error event: \"%s\" (error=%s)", m_media_player ? m_media_player->errorString() : "", error); + music_log.error("Error event: \"%s\" (error=%s)", errorString, error); } diff --git a/rpcs3/rpcs3qt/qt_music_error_handler.h b/rpcs3/rpcs3qt/qt_music_error_handler.h index 11c5ff3fbd..7d19c22d5c 100644 --- a/rpcs3/rpcs3qt/qt_music_error_handler.h +++ b/rpcs3/rpcs3qt/qt_music_error_handler.h @@ -13,8 +13,8 @@ public: private Q_SLOTS: void handle_media_status(QMediaPlayer::MediaStatus status); - void handle_music_state(QMediaPlayer::State state); - void handle_music_error(QMediaPlayer::Error error); + void handle_music_state(QMediaPlayer::PlaybackState state); + void handle_music_error(QMediaPlayer::Error error, const QString& errorString); private: std::shared_ptr m_media_player; diff --git a/rpcs3/rpcs3qt/qt_music_handler.cpp b/rpcs3/rpcs3qt/qt_music_handler.cpp index 39c40a5915..d92d379cd9 100644 --- a/rpcs3/rpcs3qt/qt_music_handler.cpp +++ b/rpcs3/rpcs3qt/qt_music_handler.cpp @@ -4,6 +4,7 @@ #include "Utilities/Thread.h" #include "util/logs.hpp" +#include #include LOG_CHANNEL(music_log, "Music"); @@ -13,7 +14,7 @@ qt_music_handler::qt_music_handler() music_log.notice("Constructing Qt music handler..."); m_media_player = std::make_shared(); - m_media_player->setAudioRole(QAudio::Role::MusicRole); + m_media_player->setAudioOutput(new QAudioOutput()); m_error_handler = std::make_unique(m_media_player, [this](QMediaPlayer::MediaStatus status) @@ -25,7 +26,6 @@ qt_music_handler::qt_music_handler() switch (status) { - case QMediaPlayer::MediaStatus::UnknownMediaStatus: case QMediaPlayer::MediaStatus::NoMedia: case QMediaPlayer::MediaStatus::LoadingMedia: case QMediaPlayer::MediaStatus::LoadedMedia: @@ -90,7 +90,7 @@ void qt_music_handler::play(const std::string& path) if (m_path != path) { m_path = path; - m_media_player->setMedia(QUrl(QString::fromStdString(path))); + m_media_player->setSource(QUrl::fromLocalFile(QString::fromStdString(path))); } music_log.notice("Playing music: %s", path); @@ -110,7 +110,7 @@ void qt_music_handler::fast_forward(const std::string& path) if (m_path != path) { m_path = path; - m_media_player->setMedia(QUrl(QString::fromStdString(path))); + m_media_player->setSource(QUrl::fromLocalFile(QString::fromStdString(path))); } music_log.notice("Fast-forwarding music..."); @@ -130,7 +130,7 @@ void qt_music_handler::fast_reverse(const std::string& path) if (m_path != path) { m_path = path; - m_media_player->setMedia(QUrl(QString::fromStdString(path))); + m_media_player->setSource(QUrl::fromLocalFile(QString::fromStdString(path))); } music_log.notice("Fast-reversing music..."); @@ -149,7 +149,7 @@ void qt_music_handler::set_volume(f32 volume) { const int new_volume = std::max(0, std::min(volume * 100, 100)); music_log.notice("Setting volume to %d%%", new_volume); - m_media_player->setVolume(new_volume); + m_media_player->audioOutput()->setVolume(new_volume); }); } @@ -160,9 +160,8 @@ f32 qt_music_handler::get_volume() const Emu.BlockingCallFromMainThread([&volume, this]() { - const int current_volume = std::max(0, std::min(m_media_player->volume(), 100)); - music_log.notice("Getting volume: %d%%", current_volume); - volume = current_volume / 100.0f; + volume = std::max(0.f, std::min(m_media_player->audioOutput()->volume(), 1.f)); + music_log.notice("Getting volume: %d%%", volume); }); return volume; diff --git a/rpcs3/rpcs3qt/qt_utils.h b/rpcs3/rpcs3qt/qt_utils.h index 6aa6032242..197898a5be 100644 --- a/rpcs3/rpcs3qt/qt_utils.h +++ b/rpcs3/rpcs3qt/qt_utils.h @@ -142,7 +142,7 @@ namespace gui template void stop_future_watcher(QFutureWatcher& watcher, bool cancel, std::shared_ptr> cancel_flag = nullptr) { - if (watcher.isPaused() || watcher.isRunning()) + if (watcher.isSuspended() || watcher.isRunning()) { watcher.resume(); diff --git a/rpcs3/rpcs3qt/register_editor_dialog.cpp b/rpcs3/rpcs3qt/register_editor_dialog.cpp index d941ee7762..98da5fe7da 100644 --- a/rpcs3/rpcs3qt/register_editor_dialog.cpp +++ b/rpcs3/rpcs3qt/register_editor_dialog.cpp @@ -156,7 +156,7 @@ register_editor_dialog::register_editor_dialog(QWidget *parent, CPUDisAsm* _disa connect(button_cancel, &QAbstractButton::clicked, this, ®ister_editor_dialog::reject); connect(m_register_combo, &QComboBox::currentTextChanged, this, [this](const QString&) { - if (const auto qvar = m_register_combo->currentData(); qvar.canConvert(QMetaType::Int)) + if (const auto qvar = m_register_combo->currentData(); qvar.canConvert()) { updateRegister(qvar.toInt()); } diff --git a/rpcs3/rpcs3qt/save_manager_dialog.cpp b/rpcs3/rpcs3qt/save_manager_dialog.cpp index c6dda8f9b6..c07568fe94 100644 --- a/rpcs3/rpcs3qt/save_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/save_manager_dialog.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index e4edcd4681..4952831e99 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -1,5 +1,6 @@ #include -#include +#include +#include #include #include #include @@ -10,7 +11,6 @@ #include #include #include -#include #include "gui_settings.h" #include "display_sleep_control.h" @@ -1191,10 +1191,10 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std const std::string selected_camera = m_emu_settings->GetSetting(emu_settings_type::CameraID); ui->cameraIdBox->addItem(tr("None", "Camera Device"), ""); ui->cameraIdBox->addItem(tr("Default", "Camera Device"), qstr(default_camera)); - for (const QCameraInfo& camera_info : QCameraInfo::availableCameras()) + for (const QCameraDevice& camera_info : QMediaDevices::videoInputs()) { if (!camera_info.isNull()) - ui->cameraIdBox->addItem(camera_info.description(), camera_info.deviceName()); + ui->cameraIdBox->addItem(camera_info.description(), camera_info.id()); } if (const int index = ui->cameraIdBox->findData(qstr(selected_camera)); index >= 0) { diff --git a/rpcs3/rpcs3qt/trophy_manager_dialog.cpp b/rpcs3/rpcs3qt/trophy_manager_dialog.cpp index 3dd700274c..8e0fcf1ec0 100644 --- a/rpcs3/rpcs3qt/trophy_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/trophy_manager_dialog.cpp @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include diff --git a/rpcs3/rpcs3qt/user_manager_dialog.cpp b/rpcs3/rpcs3qt/user_manager_dialog.cpp index c1a2f1d941..8314e6182c 100644 --- a/rpcs3/rpcs3qt/user_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/user_manager_dialog.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include "user_manager_dialog.h" #include "table_item_delegate.h" From 6cd5a7eab960608cc38652c7d9f7900d52b122da Mon Sep 17 00:00:00 2001 From: Ani Date: Mon, 31 Jul 2023 17:20:52 +0100 Subject: [PATCH 014/184] rpcs3_version: Bump to 0.0.29 --- rpcs3/rpcs3_version.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/rpcs3_version.cpp b/rpcs3/rpcs3_version.cpp index ed709d1f1a..a271cdc16a 100644 --- a/rpcs3/rpcs3_version.cpp +++ b/rpcs3/rpcs3_version.cpp @@ -28,7 +28,7 @@ namespace rpcs3 // Currently accessible by Windows and Linux build scripts, see implementations when doing MACOSX const utils::version& get_version() { - static constexpr utils::version version{ 0, 0, 28, utils::version_type::alpha, 1, RPCS3_GIT_VERSION }; + static constexpr utils::version version{ 0, 0, 29, utils::version_type::alpha, 1, RPCS3_GIT_VERSION }; return version; } From ef12da774e12548b4c0ceebf478b66d258bb8aa1 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 31 Jul 2023 21:32:37 +0200 Subject: [PATCH 015/184] rsx: fix register_vertex_printer value func --- rpcs3/Emu/RSX/rsx_decode.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/Emu/RSX/rsx_decode.h b/rpcs3/Emu/RSX/rsx_decode.h index 439937309f..97905110bf 100644 --- a/rpcs3/Emu/RSX/rsx_decode.h +++ b/rpcs3/Emu/RSX/rsx_decode.h @@ -4526,7 +4526,7 @@ struct register_vertex_printer return "uchar4"; } - static std::string value(std::string& out, u32 v) + static std::string value(u32 v) { return fmt::format("%u %u %u %u", (v & 0xff), ((v >> 8) & 0xff), ((v >> 16) & 0xff), ((v >> 24) & 0xff)); } From 70e127b219cb1758090c17aca380010eb2a27f04 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 17 Jul 2023 00:06:51 +0200 Subject: [PATCH 016/184] Update dependencies --- 3rdparty/FAudio | 2 +- 3rdparty/curl/curl | 2 +- 3rdparty/libsdl-org/SDL | 2 +- 3rdparty/xxHash | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/3rdparty/FAudio b/3rdparty/FAudio index 58cf606b5f..29a7d3a726 160000 --- a/3rdparty/FAudio +++ b/3rdparty/FAudio @@ -1 +1 @@ -Subproject commit 58cf606b5f718883e5dffbafdec44859d4e304ec +Subproject commit 29a7d3a726383a3907baf4930d2c4d4da773b023 diff --git a/3rdparty/curl/curl b/3rdparty/curl/curl index 7ab9d43720..50490c0679 160000 --- a/3rdparty/curl/curl +++ b/3rdparty/curl/curl @@ -1 +1 @@ -Subproject commit 7ab9d43720bc34d9aa351c7ca683c1668ebf8335 +Subproject commit 50490c0679fcd0e50bb3a8fbf2d9244845652cf0 diff --git a/3rdparty/libsdl-org/SDL b/3rdparty/libsdl-org/SDL index ffa78e6bea..4761467b2e 160000 --- a/3rdparty/libsdl-org/SDL +++ b/3rdparty/libsdl-org/SDL @@ -1 +1 @@ -Subproject commit ffa78e6bead23e2ba3adf8ec2367ff2218d4343c +Subproject commit 4761467b2e8cc7db3d6bc98747daca0051858f09 diff --git a/3rdparty/xxHash b/3rdparty/xxHash index 35b0373c69..bbb27a5efb 160000 --- a/3rdparty/xxHash +++ b/3rdparty/xxHash @@ -1 +1 @@ -Subproject commit 35b0373c697b5f160d3db26b1cbb45a0d5ba788c +Subproject commit bbb27a5efb85b92a0486cf361a8635715a53f6ba From bb2d7063c4c99f958ec94de3ad44a364c8e6e894 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 16 May 2023 20:24:55 +0200 Subject: [PATCH 017/184] Update ffmpeg --- .ci/build-linux.sh | 1 + .ci/build-mac.sh | 3 +- 3rdparty/CMakeLists.txt | 23 +++------------ 3rdparty/ffmpeg | 2 +- CMakeLists.txt | 1 + buildfiles/msvc/rpcs3_default.props | 2 +- rpcs3/Emu/Cell/Modules/cellAdec.cpp | 43 +++++++++++++++-------------- rpcs3/util/media_utils.cpp | 37 +++++++++++++++++++++---- 8 files changed, 64 insertions(+), 48 deletions(-) diff --git a/.ci/build-linux.sh b/.ci/build-linux.sh index a0e609efb1..3e42e6d2bb 100755 --- a/.ci/build-linux.sh +++ b/.ci/build-linux.sh @@ -40,6 +40,7 @@ cmake .. \ -DCMAKE_RANLIB="$RANLIB" \ -DUSE_SYSTEM_CURL=ON \ -DUSE_SDL=ON \ + -DUSE_SYSTEM_FFMPEG=OFF \ -DOpenGL_GL_PREFERENCE=LEGACY \ -DLLVM_DIR=/opt/llvm/lib/cmake/llvm \ -DSTATIC_LINK_LLVM=ON \ diff --git a/.ci/build-mac.sh b/.ci/build-mac.sh index 8116d93484..6276fe92d9 100755 --- a/.ci/build-mac.sh +++ b/.ci/build-mac.sh @@ -6,7 +6,7 @@ brew install -f --overwrite nasm ninja git p7zip create-dmg ccache pipenv #/usr/sbin/softwareupdate --install-rosetta --agree-to-license arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" arch -x86_64 /usr/local/bin/brew update -arch -x86_64 /usr/local/bin/brew install -f --overwrite llvm@16 sdl2 glew cmake faudio vulkan-headers +arch -x86_64 /usr/local/bin/brew install -f --overwrite llvm@16 sdl2 glew cmake faudio vulkan-headers ffmpeg arch -x86_64 /usr/local/bin/brew link -f llvm@16 # moltenvk based on commit for 1.2.4 release @@ -74,6 +74,7 @@ mkdir build && cd build || exit 1 -DUSE_ALSA=OFF \ -DUSE_PULSE=OFF \ -DUSE_AUDIOUNIT=ON \ + -DUSE_SYSTEM_FFMPEG=ON \ -DLLVM_CCACHE_BUILD=OFF \ -DLLVM_BUILD_RUNTIME=OFF \ -DLLVM_BUILD_TOOLS=OFF \ diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 021a9f16ab..1fc11fb4d9 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -267,32 +267,17 @@ if(USE_SYSTEM_FFMPEG) target_link_libraries(3rdparty_ffmpeg INTERFACE ${FFMPEG_LIBRARIES}) else() if (NOT MSVC AND WIN32) - message(STATUS "RPCS3: building ffmpeg submodule") - - include(ProcessorCount) - ProcessorCount(N) - - ExternalProject_Add(ffmpeg-mingw - DOWNLOAD_COMMAND "" - SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg - BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/ffmpeg - CONFIGURE_COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg/configure --prefix=./windows/x86_64 --arch=x86_64 --disable-avdevice --disable-programs --disable-avfilter --disable-postproc --disable-doc --disable-pthreads --enable-w32threads --disable-network --disable-everything --disable-encoders --disable-muxers --disable-hwaccels --disable-parsers --disable-protocols --enable-dxva2 --enable-static --disable-shared --enable-decoder=aac --enable-decoder=aac_latm --enable-decoder=atrac3 --enable-decoder=atrac3p --enable-decoder=mp3 --enable-decoder=pcm_s16le --enable-decoder=pcm_s8 --enable-decoder=h264 --enable-decoder=mpeg4 --enable-decoder=mpeg2video --enable-decoder=mjpeg --enable-decoder=mjpegb --enable-encoder=pcm_s16le --enable-encoder=ffv1 --enable-encoder=mpeg4 --enable-parser=h264 --enable-parser=mpeg4video --enable-parser=mpegaudio --enable-parser=mpegvideo --enable-parser=mjpeg --enable-parser=aac --enable-parser=aac_latm --enable-muxer=avi --enable-demuxer=h264 --enable-demuxer=m4v --enable-demuxer=mp3 --enable-demuxer=mpegvideo --enable-demuxer=mpegps --enable-demuxer=mjpeg --enable-demuxer=avi --enable-demuxer=aac --enable-demuxer=pmp --enable-demuxer=oma --enable-demuxer=pcm_s16le --enable-demuxer=pcm_s8 --enable-demuxer=wav --enable-hwaccel=h264_dxva2 --enable-indev=dshow --enable-protocol=file - BUILD_COMMAND make -j ${N} - INSTALL_COMMAND make install - ) - - target_link_directories(3rdparty_ffmpeg INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg/windows/x86_64/lib") - target_link_libraries(3rdparty_ffmpeg INTERFACE avformat avcodec avutil swscale swresample iconv) + message(FATAL_ERROR "-- RPCS3: building ffmpeg submodule is currently not supported") else() message(STATUS "RPCS3: using builtin ffmpeg") if (WIN32) - set(FFMPEG_LIB_DIR "ffmpeg/windows/x86_64") + set(FFMPEG_LIB_DIR "ffmpeg/lib/windows/x86_64") target_link_libraries(3rdparty_ffmpeg INTERFACE "Bcrypt.lib") elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") - set(FFMPEG_LIB_DIR "ffmpeg/linux/x86_64") + set(FFMPEG_LIB_DIR "ffmpeg/lib/linux/ubuntu-20.04/x86_64") elseif(APPLE) - set(FFMPEG_LIB_DIR "ffmpeg/macos/x86_64") + set(FFMPEG_LIB_DIR "ffmpeg/lib/macos/x86_64") else() message(FATAL_ERROR "Prebuilt ffmpeg is not available on this platform! Try USE_SYSTEM_FFMPEG=ON.") endif() diff --git a/3rdparty/ffmpeg b/3rdparty/ffmpeg index bf019f8c88..d9f2a87e81 160000 --- a/3rdparty/ffmpeg +++ b/3rdparty/ffmpeg @@ -1 +1 @@ -Subproject commit bf019f8c88bc64638fccef62840e935ab2689a4a +Subproject commit d9f2a87e8112d1c1217adabb0dc945d8ad2da657 diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c6eadc999..8887c21d36 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,7 @@ option(USE_VULKAN "Vulkan render backend" ON) option(USE_PRECOMPILED_HEADERS "Use precompiled headers" OFF) option(USE_SDL "Enables SDL input handler" OFF) option(USE_SYSTEM_SDL "Prefer system SDL instead of the builtin one" OFF) +option(USE_SYSTEM_FFMPEG "Prefer system ffmpeg instead of the prebuild one" OFF) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/buildfiles/cmake") diff --git a/buildfiles/msvc/rpcs3_default.props b/buildfiles/msvc/rpcs3_default.props index b3ac8aae1c..6f5030c91d 100644 --- a/buildfiles/msvc/rpcs3_default.props +++ b/buildfiles/msvc/rpcs3_default.props @@ -24,7 +24,7 @@
xxhash.lib;ws2_32.lib;Iphlpapi.lib;Bcrypt.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib - ..\3rdparty\ffmpeg\windows\x86_64 + ..\3rdparty\ffmpeg\lib\windows\x86_64 8388608 1048576 diff --git a/rpcs3/Emu/Cell/Modules/cellAdec.cpp b/rpcs3/Emu/Cell/Modules/cellAdec.cpp index 77deedc265..5a62e03b11 100644 --- a/rpcs3/Emu/Cell/Modules/cellAdec.cpp +++ b/rpcs3/Emu/Cell/Modules/cellAdec.cpp @@ -588,10 +588,10 @@ public: frame.auAddr = task.au.addr; frame.auSize = task.au.size; frame.userdata = task.au.userdata; - frame.size = frame.data->nb_samples * frame.data->channels * nbps; + frame.size = frame.data->nb_samples * frame.data->ch_layout.nb_channels * nbps; //cellAdec.notice("got audio frame (pts=0x%llx, nb_samples=%d, ch=%d, sample_rate=%d, nbps=%d)", - //frame.pts, frame.data->nb_samples, frame.data->channels, frame.data->sample_rate, nbps); + //frame.pts, frame.data->nb_samples, frame.data->ch_layout.nb_channels, frame.data->sample_rate, nbps); if (frames.push(frame, &is_closed)) { @@ -944,7 +944,7 @@ error_code cellAdecGetPcm(u32 handle, vm::ptr outBuffer) if (outBuffer) { // reverse byte order: - if (frame->format == AV_SAMPLE_FMT_FLTP && frame->channels == 1) + if (frame->format == AV_SAMPLE_FMT_FLTP && frame->ch_layout.nb_channels == 1) { float* in_f = reinterpret_cast(frame->extended_data[0]); for (u32 i = 0; i < af.size / 4; i++) @@ -952,7 +952,7 @@ error_code cellAdecGetPcm(u32 handle, vm::ptr outBuffer) outBuffer[i] = in_f[i]; } } - else if (frame->format == AV_SAMPLE_FMT_FLTP && frame->channels == 2) + else if (frame->format == AV_SAMPLE_FMT_FLTP && frame->ch_layout.nb_channels == 2) { float* in_f[2]; in_f[0] = reinterpret_cast(frame->extended_data[0]); @@ -963,7 +963,7 @@ error_code cellAdecGetPcm(u32 handle, vm::ptr outBuffer) outBuffer[i * 2 + 1] = in_f[1][i]; } } - else if (frame->format == AV_SAMPLE_FMT_FLTP && frame->channels == 6) + else if (frame->format == AV_SAMPLE_FMT_FLTP && frame->ch_layout.nb_channels == 6) { float* in_f[6]; in_f[0] = reinterpret_cast(frame->extended_data[0]); @@ -982,7 +982,7 @@ error_code cellAdecGetPcm(u32 handle, vm::ptr outBuffer) outBuffer[i * 6 + 5] = in_f[5][i]; } } - else if (frame->format == AV_SAMPLE_FMT_FLTP && frame->channels == 8) + else if (frame->format == AV_SAMPLE_FMT_FLTP && frame->ch_layout.nb_channels == 8) { float* in_f[8]; in_f[0] = reinterpret_cast(frame->extended_data[0]); @@ -1005,7 +1005,7 @@ error_code cellAdecGetPcm(u32 handle, vm::ptr outBuffer) outBuffer[i * 8 + 7] = in_f[7][i]; } } - else if (frame->format == AV_SAMPLE_FMT_S16P && frame->channels == 1) + else if (frame->format == AV_SAMPLE_FMT_S16P && frame->ch_layout.nb_channels == 1) { s16* in_i = reinterpret_cast(frame->extended_data[0]); for (u32 i = 0; i < af.size / 2; i++) @@ -1013,7 +1013,7 @@ error_code cellAdecGetPcm(u32 handle, vm::ptr outBuffer) outBuffer[i] = in_i[i] / 32768.f; } } - else if (frame->format == AV_SAMPLE_FMT_S16P && frame->channels == 2) + else if (frame->format == AV_SAMPLE_FMT_S16P && frame->ch_layout.nb_channels == 2) { s16* in_i[2]; in_i[0] = reinterpret_cast(frame->extended_data[0]); @@ -1026,7 +1026,7 @@ error_code cellAdecGetPcm(u32 handle, vm::ptr outBuffer) } else { - fmt::throw_exception("Unsupported frame format (channels=%d, format=%d)", frame->channels, frame->format); + fmt::throw_exception("Unsupported frame format (channels=%d, format=%d)", frame->ch_layout.nb_channels, frame->format); } } @@ -1078,25 +1078,26 @@ error_code cellAdecGetPcmItem(u32 handle, vm::pptr pcmItem) atx->samplingFreq = frame->sample_rate; atx->nbytes = frame->nb_samples * u32{sizeof(float)}; - if (frame->channels == 1) + + switch (frame->ch_layout.nb_channels) { - atx->channelConfigIndex = 1; - } - else if (frame->channels == 2) + case 1: + case 2: + case 6: { - atx->channelConfigIndex = 2; + atx->channelConfigIndex = frame->ch_layout.nb_channels; + break; } - else if (frame->channels == 6) - { - atx->channelConfigIndex = 6; - } - else if (frame->channels == 8) + case 8: { atx->channelConfigIndex = 7; + break; } - else + default: { - cellAdec.fatal("cellAdecGetPcmItem(): unsupported channel count (%d)", frame->channels); + cellAdec.fatal("cellAdecGetPcmItem(): unsupported channel count (%d)", frame->ch_layout.nb_channels); + break; + } } } else if (adec->type == CELL_ADEC_TYPE_MP3) diff --git a/rpcs3/util/media_utils.cpp b/rpcs3/util/media_utils.cpp index 7eb16a7878..f99617f98f 100644 --- a/rpcs3/util/media_utils.cpp +++ b/rpcs3/util/media_utils.cpp @@ -363,14 +363,14 @@ namespace utils } const int dst_channels = 2; - const u64 dst_channel_layout = AV_CH_LAYOUT_STEREO; + const AVChannelLayout dst_channel_layout = AV_CHANNEL_LAYOUT_STEREO; const AVSampleFormat dst_format = AV_SAMPLE_FMT_FLT; int set_err = 0; - if ((set_err = av_opt_set_int(av.swr, "in_channel_count", stream->codecpar->channels, 0)) || + if ((set_err = av_opt_set_int(av.swr, "in_channel_count", stream->codecpar->ch_layout.nb_channels, 0)) || (set_err = av_opt_set_int(av.swr, "out_channel_count", dst_channels, 0)) || - (set_err = av_opt_set_channel_layout(av.swr, "in_channel_layout", stream->codecpar->channel_layout, 0)) || - (set_err = av_opt_set_channel_layout(av.swr, "out_channel_layout", dst_channel_layout, 0)) || + (set_err = av_opt_set_chlayout(av.swr, "in_channel_layout", &stream->codecpar->ch_layout, 0)) || + (set_err = av_opt_set_chlayout(av.swr, "out_channel_layout", &dst_channel_layout, 0)) || (set_err = av_opt_set_int(av.swr, "in_sample_rate", stream->codecpar->sample_rate, 0)) || (set_err = av_opt_set_int(av.swr, "out_sample_rate", sample_rate, 0)) || (set_err = av_opt_set_sample_fmt(av.swr, "in_sample_fmt", static_cast(stream->codecpar->format), 0)) || @@ -710,15 +710,42 @@ namespace utils if (!codec) return nullptr; + // Try to find a preferable output format + std::vector oformats; + void* opaque = nullptr; for (const AVOutputFormat* oformat = av_muxer_iterate(&opaque); !!oformat; oformat = av_muxer_iterate(&opaque)) { if (avformat_query_codec(oformat, codec->id, FF_COMPLIANCE_STRICT) == 1) { - return oformat->name; + media_log.notice("video_encoder: Found output format '%s'", oformat->name); + + switch (codec->id) + { + case AV_CODEC_ID_MPEG4: + if (strcmp(oformat->name, "avi") == 0) + return oformat->name; + break; + case AV_CODEC_ID_H264: + case AV_CODEC_ID_MJPEG: + // TODO + break; + default: + break; + } + + oformats.push_back(oformat); } } + // Fallback to first found format + if (!oformats.empty() && oformats.front()) + { + const AVOutputFormat* oformat = oformats.front(); + media_log.notice("video_encoder: Falling back to output format '%s'", oformat->name); + return oformat->name; + } + return nullptr; }; From 9ad7c8e95b35bfd874f51719296af477327c1ee7 Mon Sep 17 00:00:00 2001 From: Ivan Chikish Date: Mon, 31 Jul 2023 23:42:05 +0300 Subject: [PATCH 018/184] Don't require Qt 6.4.0 (works with 6.2.4) --- 3rdparty/qt6.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/qt6.cmake b/3rdparty/qt6.cmake index 900657b546..40d8e94df4 100644 --- a/3rdparty/qt6.cmake +++ b/3rdparty/qt6.cmake @@ -1,6 +1,6 @@ add_library(3rdparty_qt6 INTERFACE) -set(QT_MIN_VER 6.4.0) +set(QT_MIN_VER 6.2.4) find_package(Qt6 ${QT_MIN_VER} CONFIG COMPONENTS Widgets Concurrent Multimedia MultimediaWidgets Svg SvgWidgets) if(WIN32) From 15e2ec2cf0575caf83a1312034676002b1d0d68b Mon Sep 17 00:00:00 2001 From: Ivan Chikish Date: Wed, 2 Aug 2023 13:39:25 +0300 Subject: [PATCH 019/184] Reset broken LLCM_CCACHE_BUILD change --- 3rdparty/llvm/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/3rdparty/llvm/CMakeLists.txt b/3rdparty/llvm/CMakeLists.txt index 7194533a45..1c9552e322 100644 --- a/3rdparty/llvm/CMakeLists.txt +++ b/3rdparty/llvm/CMakeLists.txt @@ -14,8 +14,7 @@ if(WITH_LLVM) option(LLVM_INCLUDE_TESTS OFF) option(LLVM_INCLUDE_TOOLS OFF) option(LLVM_INCLUDE_UTILS OFF) - # we globally enable ccache - set(LLVM_CCACHE_BUILD OFF CACHE BOOL "Set to ON for a ccache enabled build") + option(LLVM_CCACHE_BUILD ON) if(WIN32) set(LLVM_USE_INTEL_JITEVENTS ON) From 831a9fe012193e86677673b45e77dd70d6fa5c5d Mon Sep 17 00:00:00 2001 From: Ivan Chikish Date: Mon, 31 Jul 2023 23:43:06 +0300 Subject: [PATCH 020/184] Remove thread pool Prevents implementing thread priority on Linux. --- Utilities/Thread.cpp | 134 +------------------------------------------ 1 file changed, 3 insertions(+), 131 deletions(-) diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp index 1988b4c990..314715dc60 100644 --- a/Utilities/Thread.cpp +++ b/Utilities/Thread.cpp @@ -2008,37 +2008,8 @@ thread_local DECLARE(thread_ctrl::g_tls_error_callback) = nullptr; DECLARE(thread_ctrl::g_native_core_layout) { native_core_arrangement::undefined }; -static atomic_t s_thread_bits{0}; - -static atomic_t s_thread_pool[128]{}; - void thread_base::start() { - for (u128 bits = s_thread_bits.load(); bits; bits &= bits - 1) - { - const u32 pos = utils::ctz128(bits); - - if (!s_thread_pool[pos]) - { - continue; - } - - thread_base** tls = s_thread_pool[pos].exchange(nullptr); - - if (!tls) - { - continue; - } - - // Receive "that" native thread handle, sent "this" thread_base - const u64 _self = reinterpret_cast(atomic_storage::load(*tls)); - m_thread.release(_self); - ensure(_self != reinterpret_cast(this)); - atomic_storage::store(*tls, this); - s_thread_pool[pos].notify_one(); - return; - } - #ifdef _WIN32 m_thread = ::_beginthreadex(nullptr, 0, entry_point, this, CREATE_SUSPENDED, nullptr); ensure(m_thread); @@ -2232,112 +2203,13 @@ thread_base::native_entry thread_base::finalize(u64 _self) noexcept return nullptr; } - // Try to add self to thread pool - set_name("..pool"); - - thread_ctrl::set_native_priority(0); - - thread_ctrl::set_thread_affinity_mask(0); - - std::fesetround(FE_TONEAREST); - - gv_unset_zeroing_denormals(); - - static constexpr u64 s_stop_bit = 0x8000'0000'0000'0000ull; - - static atomic_t s_pool_ctr = [] - { - std::atexit([] - { - s_pool_ctr |= s_stop_bit; - - while (/*u64 remains = */s_pool_ctr & ~s_stop_bit) - { - for (u32 i = 0; i < std::size(s_thread_pool); i++) - { - if (thread_base** ptls = s_thread_pool[i].exchange(nullptr)) - { - // Extract thread handle - const u64 _self = reinterpret_cast(*ptls); - - // Wake up a thread and make sure it's joined - s_thread_pool[i].notify_one(); - #ifdef _WIN32 - const HANDLE handle = reinterpret_cast(_self); - WaitForSingleObject(handle, INFINITE); - CloseHandle(handle); + _endthreadex(0); #else - pthread_join(reinterpret_cast(_self), nullptr); + pthread_exit(nullptr); #endif - } - } - } - }); - return 0; - }(); - - s_pool_ctr++; - - u32 pos = -1; - - while (true) - { - const auto [bits, ok] = s_thread_bits.fetch_op([](u128& bits) - { - if (~bits) [[likely]] - { - // Set lowest clear bit - bits |= bits + 1; - return true; - } - - return false; - }); - - if (ok) [[likely]] - { - pos = utils::ctz128(~bits); - break; - } - - s_thread_bits.wait(bits); - } - - const auto tls = &thread_ctrl::g_tls_this_thread; - s_thread_pool[pos] = tls; - - atomic_wait::list<2> list{}; - list.set<0>(s_pool_ctr, 0, s_stop_bit); - list.set<1>(s_thread_pool[pos], tls); - - while (s_thread_pool[pos] == tls || atomic_storage::load(*tls) == fake_self) - { - list.wait(); - - if (s_pool_ctr & s_stop_bit) - { - break; - } - } - - // Free thread pool slot - s_thread_bits.atomic_op([pos](u128& val) - { - val &= ~(u128(1) << pos); - }); - - s_thread_bits.notify_one(); - - if (--s_pool_ctr & s_stop_bit) - { - return nullptr; - } - - // Return new entry point - utils::prefetch_exec((*tls)->entry_point); - return (*tls)->entry_point; + return nullptr; } thread_base::native_entry thread_base::make_trampoline(u64(*entry)(thread_base* _base)) From d34287b2cc8899b224bf783296d01cf221b065ba Mon Sep 17 00:00:00 2001 From: Ivan Chikish Date: Mon, 31 Jul 2023 23:57:26 +0300 Subject: [PATCH 021/184] Linux: use futex_waitv syscall for atomic waiting In order to make this possible, some unnecessary features were removed. --- Utilities/Thread.cpp | 73 ++-- Utilities/Thread.h | 33 +- Utilities/cond.cpp | 6 +- Utilities/lockless.h | 30 +- Utilities/mutex.cpp | 4 +- Utilities/sync.h | 26 +- rpcs3/Emu/CPU/CPUThread.cpp | 11 +- rpcs3/Emu/Cell/Modules/cellMic.cpp | 2 +- rpcs3/Emu/Cell/Modules/cellMic.h | 2 +- rpcs3/Emu/Cell/Modules/cellMsgDialog.cpp | 4 +- rpcs3/Emu/Cell/Modules/cellMusicDecode.cpp | 2 +- rpcs3/Emu/Cell/Modules/cellSpurs.cpp | 4 +- rpcs3/Emu/Cell/Modules/cellVdec.cpp | 4 +- rpcs3/Emu/Cell/PPUThread.cpp | 16 +- rpcs3/Emu/Cell/RawSPUThread.cpp | 4 +- rpcs3/Emu/Cell/SPURecompiler.cpp | 11 +- rpcs3/Emu/Cell/SPUThread.cpp | 34 +- rpcs3/Emu/Cell/SPUThread.h | 6 +- rpcs3/Emu/Cell/lv2/lv2.cpp | 11 +- rpcs3/Emu/Cell/lv2/sys_interrupt.cpp | 2 +- rpcs3/Emu/Cell/lv2/sys_mmapper.cpp | 2 +- rpcs3/Emu/Cell/lv2/sys_spu.cpp | 8 +- rpcs3/Emu/Cell/lv2/sys_sync.h | 2 +- rpcs3/Emu/Memory/vm_reservation.h | 20 +- rpcs3/Emu/RSX/GL/GLPipelineCompiler.cpp | 2 +- .../Overlays/HomeMenu/overlay_home_menu.cpp | 2 +- rpcs3/Emu/RSX/Overlays/overlay_manager.cpp | 2 +- .../RSX/Overlays/overlay_message_dialog.cpp | 2 +- rpcs3/Emu/RSX/Overlays/overlay_osk.cpp | 4 +- .../RSX/Overlays/overlay_user_list_dialog.cpp | 4 +- rpcs3/Emu/RSX/Overlays/overlays.cpp | 4 +- rpcs3/Emu/RSX/Overlays/overlays.h | 2 +- rpcs3/Emu/RSX/RSXThread.cpp | 12 +- rpcs3/Emu/RSX/RSXThread.h | 2 +- rpcs3/Emu/RSX/VK/VKPipelineCompiler.cpp | 2 +- rpcs3/Emu/RSX/rsx_decode.h | 2 +- rpcs3/Emu/System.cpp | 16 +- rpcs3/Emu/System.h | 4 +- rpcs3/Emu/system_progress.cpp | 2 +- rpcs3/headless_application.cpp | 4 +- rpcs3/headless_application.h | 4 +- rpcs3/main.cpp | 6 + rpcs3/rpcs3qt/debugger_frame.cpp | 4 +- rpcs3/rpcs3qt/gui_application.cpp | 4 +- rpcs3/rpcs3qt/gui_application.h | 4 +- rpcs3/util/atomic.cpp | 327 +++++++----------- rpcs3/util/atomic.hpp | 217 +++--------- rpcs3/util/fifo_mutex.hpp | 28 +- rpcs3/util/media_utils.cpp | 10 +- rpcs3/util/media_utils.h | 4 +- rpcs3/util/shared_ptr.hpp | 24 +- 51 files changed, 441 insertions(+), 574 deletions(-) diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp index 314715dc60..0354da63a8 100644 --- a/Utilities/Thread.cpp +++ b/Utilities/Thread.cpp @@ -2172,14 +2172,14 @@ u64 thread_base::finalize(thread_state result_state) noexcept const u64 _self = m_thread; // Set result state (errored or finalized) - m_sync.fetch_op([&](u64& v) + m_sync.fetch_op([&](u32& v) { v &= -4; v |= static_cast(result_state); }); // Signal waiting threads - m_sync.notify_all(2); + m_sync.notify_all(); return _self; } @@ -2266,8 +2266,18 @@ thread_state thread_ctrl::state() void thread_ctrl::wait_for(u64 usec, [[maybe_unused]] bool alert /* true */) { + if (!usec) + { + return; + } + auto _this = g_tls_this_thread; + if (!alert && usec > 50000) + { + usec = 50000; + } + #ifdef __linux__ static thread_local struct linux_timer_handle_t { @@ -2296,13 +2306,13 @@ void thread_ctrl::wait_for(u64 usec, [[maybe_unused]] bool alert /* true */) } } fd_timer; - if (!alert && usec > 0 && usec <= 1000 && fd_timer != -1) + if (!alert && fd_timer != -1) { struct itimerspec timeout; u64 missed; - timeout.it_value.tv_nsec = usec * 1'000ull; - timeout.it_value.tv_sec = 0; + timeout.it_value.tv_nsec = usec % 1'000'000 * 1'000ull; + timeout.it_value.tv_sec = usec / 1'000'000; timeout.it_interval.tv_sec = 0; timeout.it_interval.tv_nsec = 0; timerfd_settime(fd_timer, 0, &timeout, NULL); @@ -2312,15 +2322,27 @@ void thread_ctrl::wait_for(u64 usec, [[maybe_unused]] bool alert /* true */) } #endif - if (_this->m_sync.bit_test_reset(2) || _this->m_taskq) + if (alert) { - return; + if (_this->m_sync.bit_test_reset(2) || _this->m_taskq) + { + return; + } } // Wait for signal and thread state abort atomic_wait::list<2> list{}; - list.set<0>(_this->m_sync, 0, 4 + 1); - list.set<1>(_this->m_taskq, nullptr); + + if (alert) + { + list.set<0>(_this->m_sync, 0); + list.set<1>(utils::bless>(&_this->m_taskq)[1], 0); + } + else + { + list.set<0>(_this->m_dummy, 0); + } + list.wait(atomic_wait_timeout{usec <= 0xffff'ffff'ffff'ffff / 1000 ? usec * 1000 : 0xffff'ffff'ffff'ffff}); } @@ -2331,29 +2353,27 @@ void thread_ctrl::wait_for_accurate(u64 usec) return; } + if (usec > 50000) + { + fmt::throw_exception("thread_ctrl::wait_for_accurate: unsupported amount"); + } + +#ifdef __linux__ + return wait_for(usec, false); +#else using namespace std::chrono_literals; const auto until = std::chrono::steady_clock::now() + 1us * usec; while (true) { -#ifdef __linux__ - // NOTE: Assumption that timer initialization has succeeded - u64 host_min_quantum = usec <= 1000 ? 10 : 50; -#else // Host scheduler quantum for windows (worst case) - // NOTE: On ps3 this function has very high accuracy constexpr u64 host_min_quantum = 500; -#endif + if (usec >= host_min_quantum) { -#ifdef __linux__ - // Do not wait for the last quantum to avoid loss of accuracy - wait_for(usec - ((usec % host_min_quantum) + host_min_quantum), false); -#else // Wait on multiple of min quantum for large durations to avoid overloading low thread cpus wait_for(usec - (usec % host_min_quantum), false); -#endif } // TODO: Determine best value for yield delay else if (usec >= host_min_quantum / 2) @@ -2374,6 +2394,7 @@ void thread_ctrl::wait_for_accurate(u64 usec) usec = (until - current).count(); } +#endif } std::string thread_ctrl::get_name_cached() @@ -2440,7 +2461,7 @@ bool thread_base::join(bool dtor) const for (u64 i = 0; (m_sync & 3) <= 1; i++) { - m_sync.wait(0, 2, timeout); + m_sync.wait(m_sync & ~2, timeout); if (m_sync & 2) { @@ -2460,7 +2481,7 @@ void thread_base::notify() { // Set notification m_sync |= 4; - m_sync.notify_one(4); + m_sync.notify_all(); } u64 thread_base::get_native_id() const @@ -2497,7 +2518,7 @@ u64 thread_base::get_cycles() { cycles = static_cast(thread_time.tv_sec) * 1'000'000'000 + thread_time.tv_nsec; #endif - if (const u64 old_cycles = m_sync.fetch_op([&](u64& v){ v &= 7; v |= (cycles << 3); }) >> 3) + if (const u64 old_cycles = m_cycles.exchange(cycles)) { return cycles - old_cycles; } @@ -2507,7 +2528,7 @@ u64 thread_base::get_cycles() } else { - return m_sync >> 3; + return m_cycles; } } @@ -2560,8 +2581,8 @@ void thread_base::exec() } // Notify waiters - ptr->exec.release(nullptr); - ptr->exec.notify_all(); + ptr->done.release(1); + ptr->done.notify_all(); } if (ptr->next) diff --git a/Utilities/Thread.h b/Utilities/Thread.h index a64d64cb99..f85f94194e 100644 --- a/Utilities/Thread.h +++ b/Utilities/Thread.h @@ -100,17 +100,19 @@ class thread_future protected: atomic_t exec{}; + atomic_t done{0}; + public: // Get reference to the atomic variable for inspection and waiting for const auto& get_wait() const { - return exec; + return done; } // Wait (preset) void wait() const { - exec.wait(nullptr); + done.wait(0); } }; @@ -131,8 +133,13 @@ private: // Thread handle (platform-specific) atomic_t m_thread{0}; - // Thread state and cycles - atomic_t m_sync{0}; + // Thread cycles + atomic_t m_cycles{0}; + + atomic_t m_dummy{0}; + + // Thread state + atomic_t m_sync{0}; // Thread name atomic_ptr m_tname; @@ -284,16 +291,22 @@ public: } atomic_wait::list list{}; - list.template set(_this->m_sync, 0, 4 + 1); - list.template set(_this->m_taskq, nullptr); + list.template set(_this->m_sync, 0); + list.template set(_this->m_taskq); setter(list); list.wait(atomic_wait_timeout{usec <= 0xffff'ffff'ffff'ffff / 1000 ? usec * 1000 : 0xffff'ffff'ffff'ffff}); } - template + template static inline void wait_on(T& wait, U old, u64 usec = -1) { - wait_on_custom<1>([&](atomic_wait::list<3>& list){ list.set<0, Op>(wait, old); }, usec); + wait_on_custom<1>([&](atomic_wait::list<3>& list) { list.template set<0>(wait, old); }, usec); + } + + template + static inline void wait_on(T& wait) + { + wait_on_custom<1>([&](atomic_wait::list<3>& list) { list.template set<0>(wait); }); } // Exit. @@ -637,7 +650,7 @@ public: { bool notify_sync = false; - if (s >= thread_state::aborting && thread::m_sync.fetch_op([](u64& v){ return !(v & 3) && (v |= 1); }).second) + if (s >= thread_state::aborting && thread::m_sync.fetch_op([](u32& v) { return !(v & 3) && (v |= 1); }).second) { notify_sync = true; } @@ -650,7 +663,7 @@ public: if (notify_sync) { // Notify after context abortion has been made so all conditions for wake-up be satisfied by the time of notification - thread::m_sync.notify_one(1); + thread::m_sync.notify_all(); } if (s == thread_state::finished) diff --git a/Utilities/cond.cpp b/Utilities/cond.cpp index 69eb2d47c6..af572d191e 100644 --- a/Utilities/cond.cpp +++ b/Utilities/cond.cpp @@ -9,7 +9,7 @@ void cond_variable::imp_wait(u32 _old, u64 _timeout) noexcept ensure(_old); // Wait with timeout - m_value.wait(_old, c_signal_mask, atomic_wait_timeout{_timeout > max_timeout ? umax : _timeout * 1000}); + m_value.wait(_old, atomic_wait_timeout{_timeout > max_timeout ? umax : _timeout * 1000}); // Cleanup m_value.atomic_op([](u32& value) @@ -47,10 +47,10 @@ void cond_variable::imp_wake(u32 _count) noexcept if (_count > 1 || ((_old + (c_signal_mask & (0 - c_signal_mask))) & c_signal_mask) == c_signal_mask) { // Resort to notify_all if signal count reached max - m_value.notify_all(c_signal_mask); + m_value.notify_all(); } else { - m_value.notify_one(c_signal_mask); + m_value.notify_one(); } } diff --git a/Utilities/lockless.h b/Utilities/lockless.h index 41b24fe739..ef19fa3f7e 100644 --- a/Utilities/lockless.h +++ b/Utilities/lockless.h @@ -2,6 +2,7 @@ #include "util/types.hpp" #include "util/atomic.hpp" +#include "util/asm.hpp" //! Simple unshrinkable array base for concurrent access. Only growths automatically. //! There is no way to know the current size. The smaller index is, the faster it's accessed. @@ -280,12 +281,17 @@ public: template class lf_queue final { - atomic_t*> m_head{nullptr}; + atomic_t m_head{0}; + + lf_queue_item* load(u64 value) const noexcept + { + return reinterpret_cast*>(value >> 16); + } // Extract all elements and reverse element order (FILO to FIFO) lf_queue_item* reverse() noexcept { - if (auto* head = m_head.load() ? m_head.exchange(nullptr) : nullptr) + if (auto* head = load(m_head) ? load(m_head.exchange(0)) : nullptr) { if (auto* prev = head->m_link) { @@ -311,35 +317,35 @@ public: ~lf_queue() { - delete m_head.load(); + delete load(m_head); } - template void wait(std::nullptr_t /*null*/ = nullptr) noexcept { - if (m_head == nullptr) + if (m_head == 0) { - m_head.template wait(nullptr); + utils::bless>(&m_head)[1].wait(0); } } const volatile void* observe() const noexcept { - return m_head.load(); + return load(m_head); } explicit operator bool() const noexcept { - return m_head != nullptr; + return m_head != 0; } template void push(Args&&... args) { - auto _old = m_head.load(); + auto oldv = m_head.load(); + auto _old = load(oldv); auto item = new lf_queue_item(_old, std::forward(args)...); - while (!m_head.compare_exchange(_old, item)) + while (!m_head.compare_exchange(oldv, reinterpret_cast(item) << 16)) { item->m_link = _old; } @@ -347,7 +353,7 @@ public: if (!_old) { // Notify only if queue was empty - m_head.notify_one(); + utils::bless>(&m_head)[1].notify_one(); } } @@ -363,7 +369,7 @@ public: lf_queue_slice pop_all_reversed() { lf_queue_slice result; - result.m_head = m_head.exchange(nullptr); + result.m_head = load(m_head.exchange(0)); return result; } diff --git a/Utilities/mutex.cpp b/Utilities/mutex.cpp index e84113d607..0cbd0df30e 100644 --- a/Utilities/mutex.cpp +++ b/Utilities/mutex.cpp @@ -74,14 +74,14 @@ void shared_mutex::imp_wait() break; } - m_value.wait(old, c_sig); + m_value.wait(old); } } void shared_mutex::imp_signal() { m_value += c_sig; - m_value.notify_one(c_sig); + m_value.notify_one(); } void shared_mutex::imp_lock(u32 val) diff --git a/Utilities/sync.h b/Utilities/sync.h index df2481108e..4e60c0f2b4 100644 --- a/Utilities/sync.h +++ b/Utilities/sync.h @@ -38,7 +38,29 @@ constexpr NTSTATUS NTSTATUS_ALERTED = 0x101; constexpr NTSTATUS NTSTATUS_TIMEOUT = 0x102; #endif -#ifndef __linux__ +#ifdef __linux__ +#ifndef SYS_futex_waitv +#if defined(ARCH_X64) || defined(ARCH_ARM64) +#define SYS_futex_waitv 449 +#endif +#endif + +#ifndef FUTEX_32 +#define FUTEX_32 2 +#endif + +#ifndef FUTEX_WAITV_MAX +#define FUTEX_WAITV_MAX 128 +#endif + +struct futex_waitv +{ + __u64 val; + __u64 uaddr; + __u32 flags; + __u32 __reserved; +}; +#else enum { FUTEX_PRIVATE_FLAG = 0, @@ -113,7 +135,7 @@ inline int futex(volatile void* uaddr, int futex_op, uint val, const timespec* t } else { - // TODO + // TODO: absolute timeout } map.erase(std::find(map.find(uaddr), map.end(), ref)); diff --git a/rpcs3/Emu/CPU/CPUThread.cpp b/rpcs3/Emu/CPU/CPUThread.cpp index 031176a948..aecf9f2a24 100644 --- a/rpcs3/Emu/CPU/CPUThread.cpp +++ b/rpcs3/Emu/CPU/CPUThread.cpp @@ -261,7 +261,7 @@ struct cpu_prof if (threads.empty()) { // Wait for messages if no work (don't waste CPU) - thread_ctrl::wait_on(registered, nullptr); + thread_ctrl::wait_on(registered); continue; } @@ -939,7 +939,7 @@ bool cpu_thread::check_state() noexcept else { // TODO: fix the workaround - g_suspend_counter.wait(ctr, -4, atomic_wait_timeout{100}); + g_suspend_counter.wait(ctr, atomic_wait_timeout{10'000}); } } else @@ -972,8 +972,7 @@ bool cpu_thread::check_state() noexcept } // Short sleep when yield flag is present alone (makes no sense when other methods which can stop thread execution have been done) - // Pass a mask of a single bit which is often unused to avoid notifications - s_dummy_atomic.wait(0, 1u << 30, atomic_wait_timeout{80'000}); + s_dummy_atomic.wait(0, atomic_wait_timeout{80'000}); } } } @@ -1010,13 +1009,13 @@ cpu_thread& cpu_thread::operator=(thread_state) if (old & cpu_flag::wait && old.none_of(cpu_flag::again + cpu_flag::exit)) { - state.notify_one(cpu_flag::exit); + state.notify_one(); if (auto thread = try_get()) { if (u32 resv = atomic_storage::load(thread->raddr)) { - vm::reservation_notifier(resv).notify_all(-128); + vm::reservation_notifier(resv).notify_all(); } } } diff --git a/rpcs3/Emu/Cell/Modules/cellMic.cpp b/rpcs3/Emu/Cell/Modules/cellMic.cpp index 60fd57734f..cfd6e427ec 100644 --- a/rpcs3/Emu/Cell/Modules/cellMic.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMic.cpp @@ -73,7 +73,7 @@ void mic_context::operator()() // Timestep in microseconds constexpr u64 TIMESTEP = 256ull * 1'000'000ull / 48000ull; u64 timeout = 0; - u64 oldvalue = 0; + u32 oldvalue = 0; while (thread_ctrl::state() != thread_state::aborting) { diff --git a/rpcs3/Emu/Cell/Modules/cellMic.h b/rpcs3/Emu/Cell/Modules/cellMic.h index aba0d8bbfa..0bd637fa90 100644 --- a/rpcs3/Emu/Cell/Modules/cellMic.h +++ b/rpcs3/Emu/Cell/Modules/cellMic.h @@ -374,7 +374,7 @@ public: static constexpr auto thread_name = "Microphone Thread"sv; protected: - atomic_t wakey = 0; + atomic_t wakey = 0; // u32 signalStateLocalTalk = 9; // value is in range 0-10. 10 indicates talking, 0 indicating none. // u32 signalStateFarTalk = 0; // value is in range 0-10. 10 indicates talking from far away, 0 indicating none. diff --git a/rpcs3/Emu/Cell/Modules/cellMsgDialog.cpp b/rpcs3/Emu/Cell/Modules/cellMsgDialog.cpp index 38e8179e4b..1204ff1773 100644 --- a/rpcs3/Emu/Cell/Modules/cellMsgDialog.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMsgDialog.cpp @@ -164,7 +164,7 @@ error_code open_msg_dialog(bool is_blocking, u32 type, vm::cptr msgString, return CellSysutilError{ret + 0u}; } - const auto notify = std::make_shared>(false); + const auto notify = std::make_shared>(0); const auto res = manager->create()->show(is_blocking, msgString.get_ptr(), _type, [callback, userData, &return_code, is_blocking, notify](s32 status) { @@ -186,7 +186,7 @@ error_code open_msg_dialog(bool is_blocking, u32 type, vm::cptr msgString, if (is_blocking && notify) { - *notify = true; + *notify = 1; notify->notify_one(); } }); diff --git a/rpcs3/Emu/Cell/Modules/cellMusicDecode.cpp b/rpcs3/Emu/Cell/Modules/cellMusicDecode.cpp index 1ad732df55..0ae0cd80b4 100644 --- a/rpcs3/Emu/Cell/Modules/cellMusicDecode.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMusicDecode.cpp @@ -256,7 +256,7 @@ error_code cell_music_decode_read(vm::ptr buf, vm::ptr startTime, u64 { dec.read_pos = 0; dec.decoder.clear(); - dec.decoder.track_fully_consumed = true; + dec.decoder.track_fully_consumed = 1; dec.decoder.track_fully_consumed.notify_one(); break; } diff --git a/rpcs3/Emu/Cell/Modules/cellSpurs.cpp b/rpcs3/Emu/Cell/Modules/cellSpurs.cpp index 721a1c82a3..3d5c535058 100644 --- a/rpcs3/Emu/Cell/Modules/cellSpurs.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSpurs.cpp @@ -2477,8 +2477,8 @@ s32 _spurs::add_workload(ppu_thread& ppu, vm::ptr spurs, vm::ptr spurs_res += 127; spurs_res2 += 127; - spurs_res.notify_all(-128); - spurs_res2.notify_all(-128); + spurs_res.notify_all(); + spurs_res2.notify_all(); u32 res_wkl; const auto wkl = &spurs->wklInfo(wnum); diff --git a/rpcs3/Emu/Cell/Modules/cellVdec.cpp b/rpcs3/Emu/Cell/Modules/cellVdec.cpp index d7f4fd2f0e..cb73b918c0 100644 --- a/rpcs3/Emu/Cell/Modules/cellVdec.cpp +++ b/rpcs3/Emu/Cell/Modules/cellVdec.cpp @@ -303,7 +303,7 @@ struct vdec_context final return; } - thread_ctrl::wait_on(in_cmd, nullptr); + thread_ctrl::wait_on(in_cmd); slice = in_cmd.pop_all(); // Pop new command list }()) { @@ -921,7 +921,7 @@ static error_code vdecOpen(ppu_thread& ppu, T type, U res, vm::cptr }); thrd->state -= cpu_flag::stop; - thrd->state.notify_one(cpu_flag::stop); + thrd->state.notify_one(); return CELL_OK; } diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 4b09778a00..8619dcd81e 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -1821,7 +1821,11 @@ void ppu_thread::cpu_task() // Wait until the progress dialog is closed. // We don't want to open a cell dialog while a native progress dialog is still open. - thread_ctrl::wait_on(g_progr_ptotal, 0); + while (u32 v = g_progr_ptotal) + { + g_progr_ptotal.wait(v); + } + g_fxo->get().show_overlay_message_only = true; // Sadly we can't postpone initializing guest time because we need to run PPU threads @@ -1839,7 +1843,7 @@ void ppu_thread::cpu_task() } ensure(spu.state.test_and_reset(cpu_flag::stop)); - spu.state.notify_one(cpu_flag::stop); + spu.state.notify_one(); } }); @@ -2051,7 +2055,7 @@ ppu_thread::ppu_thread(utils::serial& ar) struct init_pushed { bool pushed = false; - atomic_t inited = false; + atomic_t inited = false; }; call_history.data.resize(g_cfg.core.ppu_call_history ? call_history_max_size : 1); @@ -2100,7 +2104,7 @@ ppu_thread::ppu_thread(utils::serial& ar) { while (!Emu.IsStopped() && !g_fxo->get().inited) { - thread_ctrl::wait_on(g_fxo->get().inited, false); + thread_ctrl::wait_on(g_fxo->get().inited, 0); } return false; } @@ -2117,7 +2121,7 @@ ppu_thread::ppu_thread(utils::serial& ar) {ppu_cmd::ptr_call, 0}, +[](ppu_thread&) -> bool { auto& inited = g_fxo->get().inited; - inited = true; + inited = 1; inited.notify_all(); return true; } @@ -3046,7 +3050,7 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) if (ppu.cia < liblv2_begin || ppu.cia >= liblv2_end) { - res.notify_all(-128); + res.notify_all(); } if (addr == ppu.last_faddr) diff --git a/rpcs3/Emu/Cell/RawSPUThread.cpp b/rpcs3/Emu/Cell/RawSPUThread.cpp index 580ee319a9..eaf5e2dbc1 100644 --- a/rpcs3/Emu/Cell/RawSPUThread.cpp +++ b/rpcs3/Emu/Cell/RawSPUThread.cpp @@ -21,7 +21,7 @@ inline void try_start(spu_thread& spu) }).second) { spu.state -= cpu_flag::stop; - spu.state.notify_one(cpu_flag::stop); + spu.state.notify_one(); } }; @@ -273,7 +273,7 @@ bool spu_thread::write_reg(const u32 addr, const u32 value) for (status_npc_sync_var old; (old = status_npc).status & SPU_STATUS_RUNNING;) { - status_npc.wait(old); + utils::bless>(&status_npc)[0].wait(old.status); } } } diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index 5c88bba127..be20bcb8ef 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -647,7 +647,10 @@ void spu_cache::initialize() if (g_cfg.core.spu_decoder == spu_decoder_type::asmjit || g_cfg.core.spu_decoder == spu_decoder_type::llvm) { // Initialize progress dialog (wait for previous progress done) - thread_ctrl::wait_on(g_progr_ptotal, 0); + while (u32 v = g_progr_ptotal) + { + g_progr_ptotal.wait(v); + } g_progr_ptotal += ::size32(func_list); progr.emplace("Building SPU cache..."); @@ -7795,7 +7798,7 @@ public: { minusb = eval(x); } - + const auto minusbx = bitcast(minusb); // Data with swapped endian from a load instruction @@ -11011,7 +11014,7 @@ struct spu_llvm_worker return; } - thread_ctrl::wait_on(registered, nullptr); + thread_ctrl::wait_on(utils::bless>(®istered)[1], 0); slice = registered.pop_all(); }()) { @@ -11178,7 +11181,7 @@ struct spu_llvm { // Interrupt profiler thread and put it to sleep static_cast(prof_mutex.reset()); - thread_ctrl::wait_on(registered, nullptr); + thread_ctrl::wait_on(utils::bless>(®istered)[1], 0); continue; } diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index f24f41ef3a..4fdd72f143 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -2418,7 +2418,7 @@ void spu_thread::do_dma_transfer(spu_thread* _this, const spu_mfc_cmd& args, u8* } } - if (++i < 10) + if (true || ++i < 10) { busy_wait(500); } @@ -2426,7 +2426,7 @@ void spu_thread::do_dma_transfer(spu_thread* _this, const spu_mfc_cmd& args, u8* { // Wait _cpu->state += cpu_flag::wait + cpu_flag::temp; - bits->wait(old, wmask); + // bits->wait(old, wmask); _cpu->check_state(); } }()) @@ -2542,7 +2542,7 @@ void spu_thread::do_dma_transfer(spu_thread* _this, const spu_mfc_cmd& args, u8* v &= ~wmask; }); - bits->notify_all(wmask); + // bits->notify_all(wmask); if (size == size0) { @@ -3588,7 +3588,7 @@ bool spu_thread::do_putllc(const spu_mfc_cmd& args) { if (raddr) { - vm::reservation_notifier(addr).notify_all(-128); + vm::reservation_notifier(addr).notify_all(); raddr = 0; } @@ -3775,7 +3775,7 @@ void spu_thread::do_putlluc(const spu_mfc_cmd& args) } do_cell_atomic_128_store(addr, _ptr(args.lsa & 0x3ff80)); - vm::reservation_notifier(addr).notify_all(-128); + vm::reservation_notifier(addr).notify_all(); } bool spu_thread::do_mfc(bool can_escape, bool must_finish) @@ -4908,7 +4908,11 @@ s64 spu_thread::get_ch_value(u32 ch) } } +#ifdef __linux__ + const bool reservation_busy_waiting = false; +#else const bool reservation_busy_waiting = ((utils::get_tsc() >> 8) % 100 + ((raddr == spurs_addr) ? 50 : 0)) < g_cfg.core.spu_reservation_busy_waiting_percentage; +#endif for (; !events.count; events = get_events(mask1 & ~SPU_EVENT_LR, true, true)) { @@ -4930,8 +4934,11 @@ s64 spu_thread::get_ch_value(u32 ch) if (raddr && (mask1 & ~SPU_EVENT_TM) == SPU_EVENT_LR) { // Don't busy-wait with TSX - memory is sensitive - if (!reservation_busy_waiting) + if (g_use_rtm || !reservation_busy_waiting) { +#ifdef __linux__ + vm::reservation_notifier(raddr).wait(rtime, atomic_wait_timeout{50'000}); +#else if (raddr - spurs_addr <= 0x80 && !g_cfg.core.spu_accurate_reservations && mask1 == SPU_EVENT_LR) { atomic_wait_engine::set_one_time_use_wait_callback(+[](u64) -> bool @@ -4944,7 +4951,7 @@ s64 spu_thread::get_ch_value(u32 ch) // Wait without timeout, in this situation we have notifications for all writes making it possible // Abort notifications are handled specially for performance reasons - vm::reservation_notifier(raddr).wait(rtime, -128); + vm::reservation_notifier(raddr).wait(rtime); continue; } @@ -4976,7 +4983,8 @@ s64 spu_thread::get_ch_value(u32 ch) return true; }); - vm::reservation_notifier(raddr).wait(rtime, -128, atomic_wait_timeout{80'000}); + vm::reservation_notifier(raddr).wait(rtime, atomic_wait_timeout{80'000}); +#endif } else { @@ -5464,7 +5472,7 @@ extern void resume_spu_thread_group_from_waiting(spu_thread& spu) { group->run_state = SPU_THREAD_GROUP_STATUS_SUSPENDED; spu.state += cpu_flag::signal; - spu.state.notify_one(cpu_flag::signal); + spu.state.notify_one(); return; } @@ -5482,7 +5490,7 @@ extern void resume_spu_thread_group_from_waiting(spu_thread& spu) thread->state -= cpu_flag::suspend; } - thread->state.notify_one(cpu_flag::suspend + cpu_flag::signal); + thread->state.notify_one(); } } } @@ -6244,7 +6252,7 @@ s64 spu_channel::pop_wait(cpu_thread& spu, bool pop) while (true) { - thread_ctrl::wait_on(data, bit_wait); + thread_ctrl::wait_on(utils::bless>(&data)[1], u32{bit_wait >> 32}); old = data; if (!(old & bit_wait)) @@ -6325,7 +6333,7 @@ bool spu_channel::push_wait(cpu_thread& spu, u32 value, bool push) return false; } - thread_ctrl::wait_on(data, state); + thread_ctrl::wait_on(utils::bless>(&data)[1], u32(state >> 32)); state = data; } } @@ -6369,7 +6377,7 @@ std::pair spu_channel_4_t::pop_wait(cpu_thread& spu) while (true) { - thread_ctrl::wait_on(values, old); + thread_ctrl::wait_on(utils::bless>(&values)[0], u32(u64(std::bit_cast(old)))); old = values; if (!old.waiting) diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 4801c7671c..92ed38c66e 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -235,7 +235,7 @@ public: // Turn off waiting bit manually (must succeed because waiting bit can only be resetted by the thread pushed to jostling_value) ensure(this->data.bit_test_reset(off_wait)); - data.notify_one(); + utils::bless>(&data)[1].notify_one(); } // Return true if count has changed from 0 to 1, this condition is considered satisfied even if we pushed a value directly to the special storage for waiting SPUs @@ -294,7 +294,7 @@ public: if ((old & mask) == mask) { - data.notify_one(); + utils::bless>(&data)[1].notify_one(); } return static_cast(old); @@ -386,7 +386,7 @@ struct spu_channel_4_t // Turn off waiting bit manually (must succeed because waiting bit can only be resetted by the thread pushing to jostling_value) ensure(atomic_storage::exchange(values.raw().waiting, 0)); - values.notify_one(); + utils::bless>(&values)[0].notify_one(); } return; diff --git a/rpcs3/Emu/Cell/lv2/lv2.cpp b/rpcs3/Emu/Cell/lv2/lv2.cpp index 4b775867d3..9b2473ec2b 100644 --- a/rpcs3/Emu/Cell/lv2/lv2.cpp +++ b/rpcs3/Emu/Cell/lv2/lv2.cpp @@ -1631,7 +1631,7 @@ bool lv2_obj::awake_unlocked(cpu_thread* cpu, s32 prio) if (is_paused(target->state - cpu_flag::suspend)) { - target->state.notify_one(cpu_flag::suspend); + target->state.notify_one(); } } } @@ -1684,7 +1684,7 @@ void lv2_obj::schedule_all(u64 current_time) if (notify_later_idx == std::size(g_to_notify)) { // Out of notification slots, notify locally (resizable container is not worth it) - target->state.notify_one(cpu_flag::signal + cpu_flag::suspend); + target->state.notify_one(); } else { @@ -1718,7 +1718,7 @@ void lv2_obj::schedule_all(u64 current_time) if (notify_later_idx == std::size(g_to_notify)) { // Out of notification slots, notify locally (resizable container is not worth it) - target->state.notify_one(cpu_flag::notify); + target->state.notify_one(); } else { @@ -1948,7 +1948,7 @@ bool lv2_obj::wait_timeout(u64 usec, ppu_thread* cpu, bool scale, bool is_usleep u64 remaining = usec - passed; #ifdef __linux__ // NOTE: Assumption that timer initialization has succeeded - u64 host_min_quantum = is_usleep && remaining <= 1000 ? 10 : 50; + constexpr u64 host_min_quantum = 10; #else // Host scheduler quantum for windows (worst case) // NOTE: On ps3 this function has very high accuracy @@ -1965,8 +1965,7 @@ bool lv2_obj::wait_timeout(u64 usec, ppu_thread* cpu, bool scale, bool is_usleep if (remaining > host_min_quantum) { #ifdef __linux__ - // Do not wait for the last quantum to avoid loss of accuracy - wait_for(remaining - ((remaining % host_min_quantum) + host_min_quantum)); + wait_for(remaining); #else // Wait on multiple of min quantum for large durations to avoid overloading low thread cpus wait_for(remaining - (remaining % host_min_quantum)); diff --git a/rpcs3/Emu/Cell/lv2/sys_interrupt.cpp b/rpcs3/Emu/Cell/lv2/sys_interrupt.cpp index f18fb96502..c2e4ebcd9a 100644 --- a/rpcs3/Emu/Cell/lv2/sys_interrupt.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_interrupt.cpp @@ -183,7 +183,7 @@ error_code _sys_interrupt_thread_establish(ppu_thread& ppu, vm::ptr ih, u32 }); it->state -= cpu_flag::stop; - it->state.notify_one(cpu_flag::stop); + it->state.notify_one(); return result; }); diff --git a/rpcs3/Emu/Cell/lv2/sys_mmapper.cpp b/rpcs3/Emu/Cell/lv2/sys_mmapper.cpp index 5ff9b62ccd..87a5d89d86 100644 --- a/rpcs3/Emu/Cell/lv2/sys_mmapper.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_mmapper.cpp @@ -862,7 +862,7 @@ error_code mmapper_thread_recover_page_fault(cpu_thread* cpu) if (cpu->state & cpu_flag::signal) { - cpu->state.notify_one(cpu_flag::signal); + cpu->state.notify_one(); } return CELL_OK; diff --git a/rpcs3/Emu/Cell/lv2/sys_spu.cpp b/rpcs3/Emu/Cell/lv2/sys_spu.cpp index 7ccbc6acb1..d69d20c2b7 100644 --- a/rpcs3/Emu/Cell/lv2/sys_spu.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_spu.cpp @@ -1042,7 +1042,7 @@ error_code sys_spu_thread_group_start(ppu_thread& ppu, u32 id) { for (; index != umax; index--) { - threads[index]->state.notify_one(cpu_flag::stop); + threads[index]->state.notify_one(); } } } notify_threads; @@ -1216,7 +1216,7 @@ error_code sys_spu_thread_group_resume(ppu_thread& ppu, u32 id) { for (; index != umax; index--) { - threads[index]->state.notify_one(cpu_flag::suspend); + threads[index]->state.notify_one(); } } } notify_threads; @@ -1397,7 +1397,7 @@ error_code sys_spu_thread_group_terminate(ppu_thread& ppu, u32 id, s32 value) if (prev_resv && prev_resv != resv) { // Batch reservation notifications if possible - vm::reservation_notifier(prev_resv).notify_all(-128); + vm::reservation_notifier(prev_resv).notify_all(); } prev_resv = resv; @@ -1407,7 +1407,7 @@ error_code sys_spu_thread_group_terminate(ppu_thread& ppu, u32 id, s32 value) if (prev_resv) { - vm::reservation_notifier(prev_resv).notify_all(-128); + vm::reservation_notifier(prev_resv).notify_all(); } group->exit_status = value; diff --git a/rpcs3/Emu/Cell/lv2/sys_sync.h b/rpcs3/Emu/Cell/lv2/sys_sync.h index 98df7cd729..28a04d281a 100644 --- a/rpcs3/Emu/Cell/lv2/sys_sync.h +++ b/rpcs3/Emu/Cell/lv2/sys_sync.h @@ -434,7 +434,7 @@ public: { // Note: by the time of notification the thread could have been deallocated which is why the direct function is used // TODO: Pass a narrower mask - atomic_wait_engine::notify_one(cpu, 4, atomic_wait::default_mask>); + atomic_wait_engine::notify_one(cpu); } } diff --git a/rpcs3/Emu/Memory/vm_reservation.h b/rpcs3/Emu/Memory/vm_reservation.h index ec6af4f7ae..6330a08a4d 100644 --- a/rpcs3/Emu/Memory/vm_reservation.h +++ b/rpcs3/Emu/Memory/vm_reservation.h @@ -135,7 +135,7 @@ namespace vm _xend(); #endif if constexpr (Ack) - res.notify_all(-128); + res.notify_all(); return; } else @@ -149,7 +149,7 @@ namespace vm _xend(); #endif if constexpr (Ack) - res.notify_all(-128); + res.notify_all(); return result; } else @@ -204,7 +204,7 @@ namespace vm #endif res += 127; if (Ack) - res.notify_all(-128); + res.notify_all(); return; } else @@ -218,7 +218,7 @@ namespace vm #endif res += 127; if (Ack) - res.notify_all(-128); + res.notify_all(); return result; } else @@ -253,7 +253,7 @@ namespace vm }); if constexpr (Ack) - res.notify_all(-128); + res.notify_all(); return; } else @@ -273,7 +273,7 @@ namespace vm }); if (Ack && result) - res.notify_all(-128); + res.notify_all(); return result; } } @@ -293,7 +293,7 @@ namespace vm } if constexpr (Ack) - res.notify_all(-128); + res.notify_all(); return; } else @@ -313,7 +313,7 @@ namespace vm } if (Ack && result) - res.notify_all(-128); + res.notify_all(); return result; } } @@ -405,7 +405,7 @@ namespace vm if constexpr (Ack) { - res.notify_all(-128); + res.notify_all(); } } else @@ -415,7 +415,7 @@ namespace vm if constexpr (Ack) { - res.notify_all(-128); + res.notify_all(); } return result; diff --git a/rpcs3/Emu/RSX/GL/GLPipelineCompiler.cpp b/rpcs3/Emu/RSX/GL/GLPipelineCompiler.cpp index ff20beb17f..445841b44b 100644 --- a/rpcs3/Emu/RSX/GL/GLPipelineCompiler.cpp +++ b/rpcs3/Emu/RSX/GL/GLPipelineCompiler.cpp @@ -55,7 +55,7 @@ namespace gl job.completion_callback(result); } - thread_ctrl::wait_on(m_work_queue, nullptr); + thread_ctrl::wait_on(m_work_queue); } } diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu.cpp b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu.cpp index ebbcb88e9a..b7b255e780 100644 --- a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu.cpp +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu.cpp @@ -161,7 +161,7 @@ namespace rsx this->on_close = std::move(on_close); visible = true; - const auto notify = std::make_shared>(false); + const auto notify = std::make_shared>(0); auto& overlayman = g_fxo->get(); overlayman.attach_thread_input( diff --git a/rpcs3/Emu/RSX/Overlays/overlay_manager.cpp b/rpcs3/Emu/RSX/Overlays/overlay_manager.cpp index 50517af3e2..1d8a6292f6 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_manager.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_manager.cpp @@ -295,7 +295,7 @@ namespace rsx } else if (!m_input_thread_abort) { - thread_ctrl::wait_on(m_input_token_stack, nullptr); + thread_ctrl::wait_on(m_input_token_stack); } } } diff --git a/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.cpp b/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.cpp index 821bf4c74a..078ccb9c85 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.cpp @@ -295,7 +295,7 @@ namespace rsx { if (!m_stop_input_loop) { - const auto notify = std::make_shared>(false); + const auto notify = std::make_shared>(0); auto& overlayman = g_fxo->get(); if (interactive) diff --git a/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp b/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp index 44c8ba6904..5bebaa226e 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp @@ -1621,7 +1621,7 @@ namespace rsx update_panel(); - const auto notify = std::make_shared>(false); + const auto notify = std::make_shared>(0); auto& overlayman = g_fxo->get(); overlayman.attach_thread_input( @@ -1631,7 +1631,7 @@ namespace rsx while (!Emu.IsStopped() && !*notify) { - notify->wait(false, atomic_wait_timeout{1'000'000}); + notify->wait(0, atomic_wait_timeout{1'000'000}); } } } diff --git a/rpcs3/Emu/RSX/Overlays/overlay_user_list_dialog.cpp b/rpcs3/Emu/RSX/Overlays/overlay_user_list_dialog.cpp index c7abe5fba2..3f8d296436 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_user_list_dialog.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_user_list_dialog.cpp @@ -240,7 +240,7 @@ namespace rsx this->on_close = std::move(on_close); visible = true; - const auto notify = std::make_shared>(false); + const auto notify = std::make_shared>(0); auto& overlayman = g_fxo->get(); overlayman.attach_thread_input( @@ -250,7 +250,7 @@ namespace rsx while (!Emu.IsStopped() && !*notify) { - notify->wait(false, atomic_wait_timeout{1'000'000}); + notify->wait(0, atomic_wait_timeout{1'000'000}); } return CELL_OK; diff --git a/rpcs3/Emu/RSX/Overlays/overlays.cpp b/rpcs3/Emu/RSX/Overlays/overlays.cpp index d4d3fb3511..e003b7fd5a 100644 --- a/rpcs3/Emu/RSX/Overlays/overlays.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlays.cpp @@ -20,7 +20,7 @@ namespace rsx u64 user_interface::alloc_thread_bit() { - auto [_old, ok] = this->thread_bits.fetch_op([](u64& bits) + auto [_old, ok] = this->thread_bits.fetch_op([](u32& bits) { if (~bits) { @@ -385,7 +385,7 @@ namespace rsx m_stop_pad_interception.release(stop_pad_interception); m_stop_input_loop.release(true); - while (u64 b = thread_bits) + while (u32 b = thread_bits) { if (b == g_thread_bit) { diff --git a/rpcs3/Emu/RSX/Overlays/overlays.h b/rpcs3/Emu/RSX/Overlays/overlays.h index 05a2074e8d..ac23e00406 100644 --- a/rpcs3/Emu/RSX/Overlays/overlays.h +++ b/rpcs3/Emu/RSX/Overlays/overlays.h @@ -85,7 +85,7 @@ namespace rsx bool m_start_pad_interception = true; atomic_t m_stop_pad_interception = false; atomic_t m_input_thread_detached = false; - atomic_t thread_bits = 0; + atomic_t thread_bits = 0; bool m_keyboard_input_enabled = false; // Allow keyboard input bool m_keyboard_pad_handler_active = true; // Initialized as true to prevent keyboard input until proven otherwise. bool m_allow_input_on_pause = false; diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index 1e9c907f40..c5f2e90161 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -853,9 +853,8 @@ namespace rsx g_fxo->get().set_thread(std::shared_ptr>>(new named_thread>("VBlank Thread"sv, [this]() -> void { - // See sys_timer_usleep for details #ifdef __linux__ - constexpr u32 host_min_quantum = 50; + constexpr u32 host_min_quantum = 10; #else constexpr u32 host_min_quantum = 500; #endif @@ -878,8 +877,12 @@ namespace rsx // Calculate time remaining to that time (0 if we passed it) const u64 wait_for = current >= post_event_time ? 0 : post_event_time - current; +#ifdef __linux__ + const u64 wait_sleep = wait_for; +#else // Substract host operating system min sleep quantom to get sleep time const u64 wait_sleep = wait_for - u64{wait_for >= host_min_quantum} * host_min_quantum; +#endif if (!wait_for) { @@ -3116,7 +3119,7 @@ namespace rsx { #ifdef __linux__ // NOTE: Assumption that timer initialization has succeeded - u64 host_min_quantum = remaining <= 1000 ? 10 : 50; + constexpr u64 host_min_quantum = 10; #else // Host scheduler quantum for windows (worst case) // NOTE: On ps3 this function has very high accuracy @@ -3125,8 +3128,7 @@ namespace rsx if (remaining >= host_min_quantum) { #ifdef __linux__ - // Do not wait for the last quantum to avoid loss of accuracy - thread_ctrl::wait_for(remaining - ((remaining % host_min_quantum) + host_min_quantum), false); + thread_ctrl::wait_for(remaining, false); #else // Wait on multiple of min quantum for large durations to avoid overloading low thread cpus thread_ctrl::wait_for(remaining - (remaining % host_min_quantum), false); diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index dde75f3e53..ae91c502a0 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -213,7 +213,7 @@ namespace rsx u32 last_known_code_start = 0; atomic_t external_interrupt_lock{ 0 }; atomic_t external_interrupt_ack{ false }; - atomic_t is_initialized{ false }; + atomic_t is_initialized{0}; bool is_fifo_idle() const; void flush_fifo(); diff --git a/rpcs3/Emu/RSX/VK/VKPipelineCompiler.cpp b/rpcs3/Emu/RSX/VK/VKPipelineCompiler.cpp index f83d564393..6b6882f616 100644 --- a/rpcs3/Emu/RSX/VK/VKPipelineCompiler.cpp +++ b/rpcs3/Emu/RSX/VK/VKPipelineCompiler.cpp @@ -48,7 +48,7 @@ namespace vk } } - thread_ctrl::wait_on(m_work_queue, nullptr); + thread_ctrl::wait_on(m_work_queue); } } diff --git a/rpcs3/Emu/RSX/rsx_decode.h b/rpcs3/Emu/RSX/rsx_decode.h index 97905110bf..42a5fb9f11 100644 --- a/rpcs3/Emu/RSX/rsx_decode.h +++ b/rpcs3/Emu/RSX/rsx_decode.h @@ -1699,7 +1699,7 @@ struct registers_decoder static void dump(std::string& out, const decoded_type& decoded) { fmt::append(out, "Shader control: raw_value: 0x%x reg_count: %u%s%s", - decoded.shader_ctrl(), ((decoded.shader_ctrl() >> 24) & 0xFF), ((decoded.shader_ctrl() & CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT) ? " depth_replace" : ""), + decoded.shader_ctrl(), ((decoded.shader_ctrl() >> 24) & 0xFF), ((decoded.shader_ctrl() & CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT) ? " depth_replace" : ""), ((decoded.shader_ctrl() & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS) ? " 32b_exports" : "")); } }; diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index f4211e2c62..22bf01dd26 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -153,7 +153,7 @@ void fmt_class_string::format(std::string& out, u64 arg) }); } -void Emulator::CallFromMainThread(std::function&& func, atomic_t* wake_up, bool track_emu_state, u64 stop_ctr) const +void Emulator::CallFromMainThread(std::function&& func, atomic_t* wake_up, bool track_emu_state, u64 stop_ctr) const { if (!track_emu_state) { @@ -174,14 +174,14 @@ void Emulator::CallFromMainThread(std::function&& func, atomic_t* void Emulator::BlockingCallFromMainThread(std::function&& func) const { - atomic_t wake_up = false; + atomic_t wake_up = 0; CallFromMainThread(std::move(func), &wake_up); while (!wake_up) { ensure(thread_ctrl::get_current()); - wake_up.wait(false); + wake_up.wait(0); } } @@ -424,7 +424,7 @@ void Emulator::Init() make_path_verbose(dev_flash, true); make_path_verbose(dev_flash2, true); make_path_verbose(dev_flash3, true); - + if (make_path_verbose(dev_usb, true)) { make_path_verbose(dev_usb + "MUSIC/", false); @@ -2152,7 +2152,7 @@ void Emulator::RunPPU() } ensure(cpu.state.test_and_reset(cpu_flag::stop)); - cpu.state.notify_one(cpu_flag::stop); + cpu.state.notify_one(); signalled_thread = true; }); @@ -2165,7 +2165,7 @@ void Emulator::RunPPU() if (auto thr = g_fxo->try_get>()) { thr->state -= cpu_flag::stop; - thr->state.notify_one(cpu_flag::stop); + thr->state.notify_one(); } } @@ -2234,7 +2234,7 @@ void Emulator::FinalizeRunRequest() } ensure(spu.state.test_and_reset(cpu_flag::stop)); - spu.state.notify_one(cpu_flag::stop); + spu.state.notify_one(); }; if (m_savestate_extension_flags1 & SaveStateExtentionFlags1::ShouldCloseMenu) @@ -2437,7 +2437,7 @@ void Emulator::Resume() auto on_select = [](u32, cpu_thread& cpu) { cpu.state -= cpu_flag::dbg_global_pause; - cpu.state.notify_one(cpu_flag::dbg_global_pause); + cpu.state.notify_one(); }; idm::select>(on_select); diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index 15753b3631..0148907b8a 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -54,7 +54,7 @@ constexpr bool is_error(game_boot_result res) struct EmuCallbacks { - std::function, atomic_t*)> call_from_main_thread; + std::function, atomic_t*)> call_from_main_thread; std::function on_run; // (start_playtime) continuing or going ingame, so start the clock std::function on_pause; std::function on_resume; @@ -180,7 +180,7 @@ public: } // Call from the GUI thread - void CallFromMainThread(std::function&& func, atomic_t* wake_up = nullptr, bool track_emu_state = true, u64 stop_ctr = umax) const; + void CallFromMainThread(std::function&& func, atomic_t* wake_up = nullptr, bool track_emu_state = true, u64 stop_ctr = umax) const; // Blocking call from the GUI thread void BlockingCallFromMainThread(std::function&& func) const; diff --git a/rpcs3/Emu/system_progress.cpp b/rpcs3/Emu/system_progress.cpp index ea8c0086e9..6ce68a668e 100644 --- a/rpcs3/Emu/system_progress.cpp +++ b/rpcs3/Emu/system_progress.cpp @@ -68,7 +68,7 @@ void progress_dialog_server::operator()() { // Some backends like OpenGL actually initialize a lot of driver objects in the "on_init" method. // Wait for init to complete within reasonable time. Abort just in case we have hardware/driver issues. - renderer->is_initialized.wait(false, atomic_wait_timeout(5 * 1000000000ull)); + renderer->is_initialized.wait(0, atomic_wait_timeout(5 * 1000000000ull)); auto manager = g_fxo->try_get(); show_overlay_message = g_fxo->get().show_overlay_message_only; diff --git a/rpcs3/headless_application.cpp b/rpcs3/headless_application.cpp index a7bbdee44b..763987be3a 100644 --- a/rpcs3/headless_application.cpp +++ b/rpcs3/headless_application.cpp @@ -60,7 +60,7 @@ void headless_application::InitializeCallbacks() return false; }; - callbacks.call_from_main_thread = [this](std::function func, atomic_t* wake_up) + callbacks.call_from_main_thread = [this](std::function func, atomic_t* wake_up) { RequestCallFromMainThread(std::move(func), wake_up); }; @@ -166,7 +166,7 @@ void headless_application::InitializeCallbacks() /** * Using connects avoids timers being unable to be used in a non-qt thread. So, even if this looks stupid to just call func, it's succinct. */ -void headless_application::CallFromMainThread(const std::function& func, atomic_t* wake_up) +void headless_application::CallFromMainThread(const std::function& func, atomic_t* wake_up) { func(); diff --git a/rpcs3/headless_application.h b/rpcs3/headless_application.h index aa283b01fd..5208861236 100644 --- a/rpcs3/headless_application.h +++ b/rpcs3/headless_application.h @@ -30,8 +30,8 @@ private: } Q_SIGNALS: - void RequestCallFromMainThread(std::function func, atomic_t* wake_up); + void RequestCallFromMainThread(std::function func, atomic_t* wake_up); private Q_SLOTS: - static void CallFromMainThread(const std::function& func, atomic_t* wake_up); + static void CallFromMainThread(const std::function& func, atomic_t* wake_up); }; diff --git a/rpcs3/main.cpp b/rpcs3/main.cpp index a02fcf4bd7..7609c7898d 100644 --- a/rpcs3/main.cpp +++ b/rpcs3/main.cpp @@ -55,6 +55,7 @@ DYNAMIC_IMPORT("ntdll.dll", NtSetTimerResolution, NTSTATUS(ULONG DesiredResoluti #ifdef __linux__ #include #include +#include #endif #if defined(__APPLE__) @@ -443,6 +444,11 @@ int main(int argc, char** argv) const u64 intro_time = (intro_stats.ru_utime.tv_sec + intro_stats.ru_stime.tv_sec) * 1000000000ull + (intro_stats.ru_utime.tv_usec + intro_stats.ru_stime.tv_usec) * 1000ull; #endif +#ifdef __linux__ + // Set timerslack value for Linux. The default value is 50,000ns. Change this to just 1 since we value precise timers. + prctl(PR_SET_TIMERSLACK, 1, 0, 0, 0); +#endif + s_argv0 = argv[0]; // Save for report_fatal_error // Only run RPCS3 to display an error diff --git a/rpcs3/rpcs3qt/debugger_frame.cpp b/rpcs3/rpcs3qt/debugger_frame.cpp index 9a34a09959..3d7e7a35bf 100644 --- a/rpcs3/rpcs3qt/debugger_frame.cpp +++ b/rpcs3/rpcs3qt/debugger_frame.cpp @@ -1366,7 +1366,7 @@ void debugger_frame::DoStep(bool step_over) } }); - cpu->state.notify_one(s_pause_flags); + cpu->state.notify_one(); } } @@ -1412,7 +1412,7 @@ void debugger_frame::RunBtnPress() Emu.Resume(); } - cpu->state.notify_one(s_pause_flags); + cpu->state.notify_one(); m_debugger_list->EnableThreadFollowing(); } } diff --git a/rpcs3/rpcs3qt/gui_application.cpp b/rpcs3/rpcs3qt/gui_application.cpp index 244a9fa6aa..4ea300cc29 100644 --- a/rpcs3/rpcs3qt/gui_application.cpp +++ b/rpcs3/rpcs3qt/gui_application.cpp @@ -421,7 +421,7 @@ void gui_application::InitializeCallbacks() return false; }; - callbacks.call_from_main_thread = [this](std::function func, atomic_t* wake_up) + callbacks.call_from_main_thread = [this](std::function func, atomic_t* wake_up) { RequestCallFromMainThread(std::move(func), wake_up); }; @@ -792,7 +792,7 @@ void gui_application::OnChangeStyleSheetRequest() /** * Using connects avoids timers being unable to be used in a non-qt thread. So, even if this looks stupid to just call func, it's succinct. */ -void gui_application::CallFromMainThread(const std::function& func, atomic_t* wake_up) +void gui_application::CallFromMainThread(const std::function& func, atomic_t* wake_up) { func(); diff --git a/rpcs3/rpcs3qt/gui_application.h b/rpcs3/rpcs3qt/gui_application.h index d6fdb6bc9c..eac5e19d95 100644 --- a/rpcs3/rpcs3qt/gui_application.h +++ b/rpcs3/rpcs3qt/gui_application.h @@ -118,8 +118,8 @@ Q_SIGNALS: void OnEnableDiscEject(bool enabled); void OnEnableDiscInsert(bool enabled); - void RequestCallFromMainThread(std::function func, atomic_t* wake_up); + void RequestCallFromMainThread(std::function func, atomic_t* wake_up); private Q_SLOTS: - static void CallFromMainThread(const std::function& func, atomic_t* wake_up); + static void CallFromMainThread(const std::function& func, atomic_t* wake_up); }; diff --git a/rpcs3/util/atomic.cpp b/rpcs3/util/atomic.cpp index a1612b5626..f9541320e9 100644 --- a/rpcs3/util/atomic.cpp +++ b/rpcs3/util/atomic.cpp @@ -1,6 +1,7 @@ #include "atomic.hpp" #if defined(__linux__) +// This definition is unused on Linux #define USE_FUTEX #elif !defined(_WIN32) #define USE_STD @@ -40,8 +41,8 @@ namespace utils // Total number of entries. static constexpr usz s_hashtable_size = 1u << 17; -// Reference counter combined with shifted pointer (which is assumed to be 47 bit) -static constexpr uptr s_ref_mask = (1u << 17) - 1; +// Reference counter combined with shifted pointer (which is assumed to be 48 bit) +static constexpr uptr s_ref_mask = 0xffff; // Fix for silly on-first-use initializer static bool s_null_wait_cb(const void*, u64, u64){ return true; }; @@ -55,163 +56,17 @@ static thread_local bool(*s_tls_one_time_wait_cb)(u64 attempts) = nullptr; // Callback for notification functions for optimizations static thread_local void(*s_tls_notify_cb)(const void* data, u64 progress) = nullptr; -static inline bool operator &(atomic_wait::op lhs, atomic_wait::op_flag rhs) -{ - return !!(static_cast(lhs) & static_cast(rhs)); -} - // Compare data in memory with old value, and return true if they are equal -static NEVER_INLINE bool ptr_cmp(const void* data, u32 _size, u128 old128, u128 mask128, atomic_wait::info* ext = nullptr) +static NEVER_INLINE bool ptr_cmp(const void* data, u32 old, atomic_wait::info* ext = nullptr) { - using atomic_wait::op; - using atomic_wait::op_flag; - - const u8 size = static_cast(_size); - const op flag{static_cast(_size >> 8)}; - - bool result = false; - - if (size <= 8) - { - u64 new_value = 0; - u64 old_value = static_cast(old128); - u64 mask = static_cast(mask128) & (u64{umax} >> ((64 - size * 8) & 63)); - - // Don't load memory on empty mask - switch (mask ? size : 0) - { - case 0: break; - case 1: new_value = reinterpret_cast*>(data)->load(); break; - case 2: new_value = reinterpret_cast*>(data)->load(); break; - case 4: new_value = reinterpret_cast*>(data)->load(); break; - case 8: new_value = reinterpret_cast*>(data)->load(); break; - default: - { - fmt::throw_exception("Bad size (arg=0x%x)", _size); - } - } - - if (flag & op_flag::bit_not) - { - new_value = ~new_value; - } - - if (!mask) [[unlikely]] - { - new_value = 0; - old_value = 0; - } - else - { - if (flag & op_flag::byteswap) - { - switch (size) - { - case 2: - { - new_value = stx::se_storage::swap(static_cast(new_value)); - old_value = stx::se_storage::swap(static_cast(old_value)); - mask = stx::se_storage::swap(static_cast(mask)); - break; - } - case 4: - { - new_value = stx::se_storage::swap(static_cast(new_value)); - old_value = stx::se_storage::swap(static_cast(old_value)); - mask = stx::se_storage::swap(static_cast(mask)); - break; - } - case 8: - { - new_value = stx::se_storage::swap(new_value); - old_value = stx::se_storage::swap(old_value); - mask = stx::se_storage::swap(mask); - break; - } - default: - { - break; - } - } - } - - // Make most significant bit sign bit - const auto shv = std::countl_zero(mask); - new_value &= mask; - old_value &= mask; - new_value <<= shv; - old_value <<= shv; - } - - s64 news = new_value; - s64 olds = old_value; - - u64 newa = news < 0 ? (0ull - new_value) : new_value; - u64 olda = olds < 0 ? (0ull - old_value) : old_value; - - switch (op{static_cast(static_cast(flag) & 0xf)}) - { - case op::eq: result = old_value == new_value; break; - case op::slt: result = olds < news; break; - case op::sgt: result = olds > news; break; - case op::ult: result = old_value < new_value; break; - case op::ugt: result = old_value > new_value; break; - case op::alt: result = olda < newa; break; - case op::agt: result = olda > newa; break; - case op::pop: - { - // Count is taken from least significant byte and ignores some flags - const u64 count = static_cast(old128) & 0xff; - - result = count < utils::popcnt64(new_value); - break; - } - default: - { - fmt::throw_exception("ptr_cmp(): unrecognized atomic wait operation."); - } - } - } - else if (size == 16 && (flag == op::eq || flag == (op::eq | op_flag::inverse))) - { - u128 new_value = 0; - u128 old_value = old128; - u128 mask = mask128; - - // Don't load memory on empty mask - if (mask) [[likely]] - { - new_value = atomic_storage::load(*reinterpret_cast(data)); - } - - // TODO - result = !((old_value ^ new_value) & mask); - } - else if (size > 16 && !~mask128 && (flag == op::eq || flag == (op::eq | op_flag::inverse))) - { - // Interpret old128 as a pointer to the old value - ensure(!(old128 >> (64 + 17))); - - result = std::memcmp(data, reinterpret_cast(static_cast(old128)), size) == 0; - } - else - { - fmt::throw_exception("ptr_cmp(): no alternative operations are supported for non-standard atomic wait yet."); - } - - if (flag & op_flag::inverse) - { - result = !result; - } - // Check other wait variables if provided - if (result) + if (reinterpret_cast*>(data)->load() == old) { if (ext) [[unlikely]] { for (auto e = ext; e->data; e++) { - if (!ptr_cmp(e->data, e->size, e->old, e->mask)) + if (!ptr_cmp(e->data, e->old)) { return false; } @@ -283,18 +138,15 @@ namespace #endif // Essentially a fat semaphore - struct cond_handle + struct alignas(64) cond_handle { - // Combined pointer (most significant 47 bits) and ref counter (17 least significant bits) + // Combined pointer (most significant 48 bits) and ref counter (16 least significant bits) atomic_t ptr_ref; u64 tid; - u128 mask; - u128 oldv; + u32 oldv; u64 tsc0; u16 link; - u8 size; - u8 flag; atomic_t sync; #ifdef USE_STD @@ -316,7 +168,7 @@ namespace mtx.init(mtx); #endif - ensure(!ptr_ref.exchange((iptr << 17) | 1)); + ensure(!ptr_ref.exchange((iptr << 16) | 1)); } void destroy() @@ -324,10 +176,7 @@ namespace tid = 0; tsc0 = 0; link = 0; - size = 0; - flag = 0; sync.release(0); - mask = 0; oldv = 0; #ifdef USE_STD @@ -517,7 +366,7 @@ namespace // TLS storage for few allocaded "semaphores" to allow skipping initialization static thread_local tls_cond_handler s_tls_conds{}; -static u32 cond_alloc(uptr iptr, u128 mask, u32 tls_slot = -1) +static u32 cond_alloc(uptr iptr, u32 tls_slot = -1) { // Try to get cond from tls slot instead u16* ptls = tls_slot >= std::size(s_tls_conds.cond) ? nullptr : s_tls_conds.cond + tls_slot; @@ -526,8 +375,7 @@ static u32 cond_alloc(uptr iptr, u128 mask, u32 tls_slot = -1) { // Fast reinitialize const u32 id = std::exchange(*ptls, 0); - s_cond_list[id].mask = mask; - s_cond_list[id].ptr_ref.release((iptr << 17) | 1); + s_cond_list[id].ptr_ref.release((iptr << 16) | 1); return id; } @@ -581,7 +429,6 @@ static u32 cond_alloc(uptr iptr, u128 mask, u32 tls_slot = -1) const u32 id = level3 * 64 + std::countr_one(bits); // Initialize new "semaphore" - s_cond_list[id].mask = mask; s_cond_list[id].init(iptr); return id; } @@ -625,8 +472,6 @@ static void cond_free(u32 cond_id, u32 tls_slot = -1) { // Fast finalization cond->sync.release(0); - cond->size = 0; - cond->mask = 0; *ptls = static_cast(cond_id); return; } @@ -652,7 +497,7 @@ static void cond_free(u32 cond_id, u32 tls_slot = -1) s_cond_sem1.atomic_op(FN(x -= u128{1} << (level1 * 14))); } -static cond_handle* cond_id_lock(u32 cond_id, u128 mask, uptr iptr = 0) +static cond_handle* cond_id_lock(u32 cond_id, uptr iptr = 0) { bool did_ref = false; @@ -673,7 +518,7 @@ static cond_handle* cond_id_lock(u32 cond_id, u128 mask, uptr iptr = 0) return false; } - if (iptr && (val >> 17) != iptr) + if (iptr && (val >> 16) != iptr) { // Pointer mismatch return false; @@ -686,11 +531,6 @@ static cond_handle* cond_id_lock(u32 cond_id, u128 mask, uptr iptr = 0) return false; } - if (!(mask & cond->mask) && cond->size) - { - return false; - } - if (!did_ref) { val++; @@ -702,7 +542,7 @@ static cond_handle* cond_id_lock(u32 cond_id, u128 mask, uptr iptr = 0) if (ok) { // Check other fields again - if (const u32 sync_val = cond->sync; sync_val == 0 || sync_val == 3 || (cond->size && !(mask & cond->mask))) + if (const u32 sync_val = cond->sync; sync_val == 0 || sync_val == 3) { did_ref = true; continue; @@ -713,7 +553,7 @@ static cond_handle* cond_id_lock(u32 cond_id, u128 mask, uptr iptr = 0) if ((old & s_ref_mask) == s_ref_mask) { - fmt::throw_exception("Reference count limit (131071) reached in an atomic notifier."); + fmt::throw_exception("Reference count limit (%u) reached in an atomic notifier.", s_ref_mask); } break; @@ -736,8 +576,8 @@ namespace u64 bits: 24; // Allocated bits u64 prio: 24; // Reserved - u64 ref : 17; // Ref counter - u64 iptr: 47; // First pointer to use slot (to count used slots) + u64 ref : 16; // Ref counter + u64 iptr: 48; // First pointer to use slot (to count used slots) }; // Need to spare 16 bits for ref counter @@ -760,7 +600,7 @@ namespace static void slot_free(uptr ptr, atomic_t* slot, u32 tls_slot) noexcept; template - static auto slot_search(uptr iptr, u128 mask, F func) noexcept; + static auto slot_search(uptr iptr, F func) noexcept; }; static_assert(sizeof(root_info) == 64); @@ -944,7 +784,7 @@ void root_info::slot_free(uptr iptr, atomic_t* slot, u32 tls_slot) noexcept } template -FORCE_INLINE auto root_info::slot_search(uptr iptr, u128 mask, F func) noexcept +FORCE_INLINE auto root_info::slot_search(uptr iptr, F func) noexcept { u32 index = 0; [[maybe_unused]] u32 total = 0; @@ -974,7 +814,7 @@ FORCE_INLINE auto root_info::slot_search(uptr iptr, u128 mask, F func) noexcept for (u32 i = 0; i < cond_count; i++) { - if (cond_id_lock(cond_ids[i], mask, iptr)) + if (cond_id_lock(cond_ids[i], iptr)) { if (func(cond_ids[i])) { @@ -994,18 +834,82 @@ FORCE_INLINE auto root_info::slot_search(uptr iptr, u128 mask, F func) noexcept } } -SAFE_BUFFERS(void) atomic_wait_engine::wait(const void* data, u32 size, u128 old_value, u64 timeout, u128 mask, atomic_wait::info* ext) +SAFE_BUFFERS(void) +atomic_wait_engine::wait(const void* data, u32 old_value, u64 timeout, atomic_wait::info* ext) { - const auto stamp0 = utils::get_unique_tsc(); + uint ext_size = 0; - if (!s_tls_wait_cb(data, 0, stamp0)) +#ifdef __linux__ + ::timespec ts{}; + if (timeout + 1) + { + if (ext) [[unlikely]] + { + // futex_waitv uses absolute timeout + ::clock_gettime(CLOCK_MONOTONIC, &ts); + } + + ts.tv_sec += timeout / 1'000'000'000; + ts.tv_nsec += timeout % 1'000'000'000; + if (ts.tv_nsec > 1'000'000'000) + { + ts.tv_sec++; + ts.tv_nsec -= 1'000'000'000; + } + } + + futex_waitv vec[atomic_wait::max_list]{}; + vec[0].flags = FUTEX_32 | FUTEX_PRIVATE_FLAG; + vec[0].uaddr = reinterpret_cast<__u64>(data); + vec[0].val = old_value; + + if (ext) [[unlikely]] + { + for (auto e = ext; e->data; e++) + { + ext_size++; + vec[ext_size].flags = FUTEX_32 | FUTEX_PRIVATE_FLAG; + vec[ext_size].uaddr = reinterpret_cast<__u64>(e->data); + vec[ext_size].val = e->old; + } + } + + if (ext_size) [[unlikely]] + { + if (syscall(SYS_futex_waitv, +vec, ext_size + 1, 0, timeout + 1 ? &ts : nullptr, CLOCK_MONOTONIC) == -1) + { + if (errno == ENOSYS) + { + fmt::throw_exception("futex_waitv is not supported (Linux kernel is too old)"); + } + if (errno == EINVAL) + { + fmt::throw_exception("futex_waitv: bad param"); + } + } + } + else + { + if (futex(const_cast(data), FUTEX_WAIT_PRIVATE, old_value, timeout + 1 ? &ts : nullptr) == -1) + { + if (errno == EINVAL) + { + fmt::throw_exception("futex: bad param"); + } + } + } + + return; +#endif + + if (!s_tls_wait_cb(data, 0, 0)) { return; } - const uptr iptr = reinterpret_cast(data) & (~s_ref_mask >> 17); + const auto stamp0 = utils::get_unique_tsc(); - uint ext_size = 0; + const uptr iptr = reinterpret_cast(data) & (~s_ref_mask >> 16); uptr iptr_ext[atomic_wait::max_list - 1]{}; @@ -1026,18 +930,18 @@ SAFE_BUFFERS(void) atomic_wait_engine::wait(const void* data, u32 size, u128 old } } - iptr_ext[ext_size] = reinterpret_cast(e->data) & (~s_ref_mask >> 17); + iptr_ext[ext_size] = reinterpret_cast(e->data) & (~s_ref_mask >> 16); ext_size++; } } - const u32 cond_id = cond_alloc(iptr, mask, 0); + const u32 cond_id = cond_alloc(iptr, 0); u32 cond_id_ext[atomic_wait::max_list - 1]{}; for (u32 i = 0; i < ext_size; i++) { - cond_id_ext[i] = cond_alloc(iptr_ext[i], ext[i].mask, i + 1); + cond_id_ext[i] = cond_alloc(iptr_ext[i], i + 1); } const auto slot = root_info::slot_alloc(iptr); @@ -1060,8 +964,6 @@ SAFE_BUFFERS(void) atomic_wait_engine::wait(const void* data, u32 size, u128 old // Store some info for notifiers (some may be unused) cond->link = 0; - cond->size = static_cast(size); - cond->flag = static_cast(size >> 8); cond->oldv = old_value; cond->tsc0 = stamp0; @@ -1071,8 +973,6 @@ SAFE_BUFFERS(void) atomic_wait_engine::wait(const void* data, u32 size, u128 old { // Extensions point to original cond_id, copy remaining info cond_ext[i]->link = cond_id; - cond_ext[i]->size = static_cast(ext[i].size); - cond_ext[i]->flag = static_cast(ext[i].size >> 8); cond_ext[i]->oldv = ext[i].old; cond_ext[i]->tsc0 = stamp0; @@ -1105,7 +1005,7 @@ SAFE_BUFFERS(void) atomic_wait_engine::wait(const void* data, u32 size, u128 old u64 attempts = 0; - while (ptr_cmp(data, size, old_value, mask, ext)) + while (ptr_cmp(data, old_value, ext)) { if (s_tls_one_time_wait_cb) { @@ -1263,7 +1163,7 @@ SAFE_BUFFERS(void) atomic_wait_engine::wait(const void* data, u32 size, u128 old } template -static u32 alert_sema(u32 cond_id, u32 size, u128 mask) +static u32 alert_sema(u32 cond_id, u32 size) { ensure(cond_id); @@ -1271,11 +1171,11 @@ static u32 alert_sema(u32 cond_id, u32 size, u128 mask) u32 ok = 0; - if (!cond->size || mask & cond->mask) + if (true) { // Redirect if necessary const auto _old = cond; - const auto _new = _old->link ? cond_id_lock(_old->link, u128(-1)) : _old; + const auto _new = _old->link ? cond_id_lock(_old->link) : _old; if (_new && _new->tsc0 == _old->tsc0) { @@ -1336,50 +1236,58 @@ void atomic_wait_engine::set_notify_callback(void(*cb)(const void*, u64)) s_tls_notify_cb = cb; } -void atomic_wait_engine::notify_one(const void* data, u32 size, u128 mask) +void atomic_wait_engine::notify_one(const void* data) { - const uptr iptr = reinterpret_cast(data) & (~s_ref_mask >> 17); - if (s_tls_notify_cb) s_tls_notify_cb(data, 0); - root_info::slot_search(iptr, mask, [&](u32 cond_id) +#ifdef __linux__ + futex(const_cast(data), FUTEX_WAKE_PRIVATE, 1); +#else + const uptr iptr = reinterpret_cast(data) & (~s_ref_mask >> 16); + + root_info::slot_search(iptr, [&](u32 cond_id) { - if (alert_sema(cond_id, size, mask)) + if (alert_sema(cond_id, 4)) { return true; } return false; }); +#endif if (s_tls_notify_cb) s_tls_notify_cb(data, -1); } -SAFE_BUFFERS(void) atomic_wait_engine::notify_all(const void* data, u32 size, u128 mask) +SAFE_BUFFERS(void) +atomic_wait_engine::notify_all(const void* data) { - const uptr iptr = reinterpret_cast(data) & (~s_ref_mask >> 17); - if (s_tls_notify_cb) s_tls_notify_cb(data, 0); +#ifdef __linux__ + futex(const_cast(data), FUTEX_WAKE_PRIVATE, 1); +#else + const uptr iptr = reinterpret_cast(data) & (~s_ref_mask >> 16); + // Array count for batch notification u32 count = 0; // Array itself. u32 cond_ids[128]; - root_info::slot_search(iptr, mask, [&](u32 cond_id) + root_info::slot_search(iptr, [&](u32 cond_id) { if (count >= 128) { // Unusual big amount of sema: fallback to notify_one alg - alert_sema(cond_id, size, mask); + alert_sema(cond_id, 4); return false; } - u32 res = alert_sema(cond_id, size, mask); + u32 res = alert_sema(cond_id, 4); if (~res <= u16{umax}) { @@ -1395,7 +1303,7 @@ SAFE_BUFFERS(void) atomic_wait_engine::notify_all(const void* data, u32 size, u1 { const u32 cond_id = *(std::end(cond_ids) - i - 1); - if (!s_cond_list[cond_id].wakeup(size ? 1 : 2)) + if (!s_cond_list[cond_id].wakeup(1)) { *(std::end(cond_ids) - i - 1) = ~cond_id; } @@ -1434,6 +1342,7 @@ SAFE_BUFFERS(void) atomic_wait_engine::notify_all(const void* data, u32 size, u1 { cond_free(~*(std::end(cond_ids) - i - 1)); } +#endif if (s_tls_notify_cb) s_tls_notify_cb(data, -1); diff --git a/rpcs3/util/atomic.hpp b/rpcs3/util/atomic.hpp index c3132584cd..d546a9c434 100644 --- a/rpcs3/util/atomic.hpp +++ b/rpcs3/util/atomic.hpp @@ -129,54 +129,21 @@ enum class atomic_wait_timeout : u64 inf = 0xffffffffffffffff, }; +template +class lf_queue; + +namespace stx +{ + template + class atomic_ptr; +} + // Various extensions for atomic_t::wait namespace atomic_wait { // Max number of simultaneous atomic variables to wait on (can be extended if really necessary) constexpr uint max_list = 8; - enum class op : u8 - { - eq, // Wait while value is bitwise equal to - slt, // Wait while signed value is less than - sgt, // Wait while signed value is greater than - ult, // Wait while unsigned value is less than - ugt, // Wait while unsigned value is greater than - alt, // Wait while absolute value is less than - agt, // Wait while absolute value is greater than - pop, // Wait while set bit count of the value is less than - __max - }; - - static_assert(static_cast(op::__max) == 8); - - enum class op_flag : u8 - { - inverse = 1 << 4, // Perform inverse operation (negate the result) - bit_not = 1 << 5, // Perform bitwise NOT on loaded value before operation - byteswap = 1 << 6, // Perform byteswap on both arguments and masks when applicable - }; - - constexpr op_flag op_be = std::endian::native == std::endian::little ? op_flag::byteswap : op_flag{0}; - constexpr op_flag op_le = std::endian::native == std::endian::little ? op_flag{0} : op_flag::byteswap; - - constexpr op operator |(op_flag lhs, op_flag rhs) - { - return op{static_cast(static_cast(lhs) | static_cast(rhs))}; - } - - constexpr op operator |(op_flag lhs, op rhs) - { - return op{static_cast(static_cast(lhs) | static_cast(rhs))}; - } - - constexpr op operator |(op lhs, op_flag rhs) - { - return op{static_cast(static_cast(lhs) | static_cast(rhs))}; - } - - constexpr op op_ne = op::eq | op_flag::inverse; - constexpr struct any_value_t { template @@ -186,46 +153,10 @@ namespace atomic_wait } } any_value; - template - using payload_type = decltype(std::declval().observe()); - - template > - constexpr u128 default_mask = sizeof(T) <= 8 ? u128{u64{umax} >> ((64 - sizeof(T) * 8) & 63)} : u128(-1); - - template > - constexpr u128 get_value(X&, T value = T{}, ...) - { - static_assert((sizeof(T) & (sizeof(T) - 1)) == 0); - static_assert(sizeof(T) <= 16); - return std::bit_cast, T>(value); - } - struct info { const void* data; - u32 size; - u128 old; - u128 mask; - - template > - constexpr void set_value(X& a, T value = T{}) - { - old = get_value(a, value); - } - - template > - constexpr void set_mask(T value) - { - static_assert((sizeof(T) & (sizeof(T) - 1)) == 0); - static_assert(sizeof(T) <= 16); - mask = std::bit_cast, T>(value); - } - - template > - constexpr void set_mask() - { - mask = default_mask; - } + u32 old; }; template @@ -243,9 +174,9 @@ namespace atomic_wait constexpr list& operator=(const list&) noexcept = default; - template ().template wait(any_value))...>> + template ().wait(any_value))...>> constexpr list(U&... vars) - : m_info{{&vars, sizeof(vars.observe()), get_value(vars), default_mask}...} + : m_info{{&vars, 0}...} { static_assert(sizeof...(U) == Max, "Inconsistent amount of atomics."); } @@ -256,40 +187,37 @@ namespace atomic_wait static_assert(sizeof...(U) == Max, "Inconsistent amount of values."); auto* ptr = m_info; - ((ptr->template set_value(*static_cast(ptr->data), values), ptr++), ...); + (((ptr->old = std::bit_cast(values)), ptr++), ...); return *this; } - template - constexpr list& masks(U... masks) - { - static_assert(sizeof...(U) <= Max, "Too many masks."); - - auto* ptr = m_info; - ((ptr++)->template set_mask(masks), ...); - return *this; - } - - template ().template wait(any_value))>> + template ().wait(any_value))>> constexpr void set(T2& var, U value) { static_assert(Index < Max); m_info[Index].data = &var; - m_info[Index].size = sizeof(var.observe()) | (static_cast(Flags) << 8); - m_info[Index].template set_value(var, value); - m_info[Index].template set_mask(); + m_info[Index].old = std::bit_cast(value); } - template ().template wait(any_value))>> - constexpr void set(T2& var, U value, V mask) + template + constexpr void set(lf_queue& var, std::nullptr_t = nullptr) { static_assert(Index < Max); + static_assert(sizeof(var) == sizeof(uptr)); - m_info[Index].data = &var; - m_info[Index].size = sizeof(var.observe()) | (static_cast(Flags) << 8); - m_info[Index].template set_value(var, value); - m_info[Index].template set_mask(mask); + m_info[Index].data = reinterpret_cast(&var) + sizeof(u32); + m_info[Index].old = 0; + } + + template + constexpr void set(stx::atomic_ptr& var, std::nullptr_t = nullptr) + { + static_assert(Index < Max); + static_assert(sizeof(var) == sizeof(uptr)); + + m_info[Index].data = reinterpret_cast(&var) + sizeof(u32); + m_info[Index].old = 0; } // Timeout is discouraged @@ -302,7 +230,7 @@ namespace atomic_wait } }; - template ().template wait(any_value))...>> + template ().wait(any_value))...>> list(T&... vars) -> list; } @@ -322,20 +250,15 @@ private: template friend class atomic_wait::list; - static void wait(const void* data, u32 size, u128 old_value, u64 timeout, u128 mask, atomic_wait::info* ext = nullptr); + static void wait(const void* data, u32 old_value, u64 timeout, atomic_wait::info* ext = nullptr); public: - static void notify_one(const void* data, u32 size, u128 mask128); - static void notify_all(const void* data, u32 size, u128 mask128); + static void notify_one(const void* data); + static void notify_all(const void* data); static void set_wait_callback(bool(*cb)(const void* data, u64 attempts, u64 stamp0)); static void set_notify_callback(void(*cb)(const void* data, u64 progress)); - static void set_one_time_use_wait_callback(bool(*cb)(u64 progress)); - - static void notify_all(const void* data) - { - notify_all(data, 0, u128(-1)); - } + static void set_one_time_use_wait_callback(bool (*cb)(u64 progress)); }; template @@ -343,7 +266,7 @@ void atomic_wait::list::wait(atomic_wait_timeout timeout) { static_assert(!!Max, "Cannot initiate atomic wait with empty list."); - atomic_wait_engine::wait(m_info[0].data, m_info[0].size, m_info[0].old, static_cast(timeout), m_info[0].mask, m_info + 1); + atomic_wait_engine::wait(m_info[0].data, m_info[0].old, static_cast(timeout), m_info + 1); } // Helper class, provides access to compiler-specific atomic intrinsics @@ -1759,46 +1682,31 @@ public: }); } - // Timeout is discouraged - template - void wait(type old_value, atomic_wait_timeout timeout = atomic_wait_timeout::inf) const noexcept + void wait(type old_value, atomic_wait_timeout timeout = atomic_wait_timeout::inf) const + requires(sizeof(type) == 4) { - const u128 old = std::bit_cast>(old_value); - const u128 mask = atomic_wait::default_mask; - atomic_wait_engine::wait(&m_data, sizeof(T) | (static_cast(Flags) << 8), old, static_cast(timeout), mask); + atomic_wait_engine::wait(&m_data, std::bit_cast(old_value), static_cast(timeout)); } - // Overload with mask (only selected bits are checked), timeout is discouraged - template - void wait(type old_value, type mask_value, atomic_wait_timeout timeout = atomic_wait_timeout::inf) const noexcept + [[deprecated]] void wait(type old_value, atomic_wait_timeout timeout = atomic_wait_timeout::inf) const + requires(sizeof(type) == 8) { - const u128 old = std::bit_cast>(old_value); - const u128 mask = std::bit_cast>(mask_value); - atomic_wait_engine::wait(&m_data, sizeof(T) | (static_cast(Flags) << 8), old, static_cast(timeout), mask); + atomic_wait::info ext[2]{}; + ext[0].data = reinterpret_cast(&m_data) + 4; + ext[0].old = std::bit_cast(old_value) >> 32; + atomic_wait_engine::wait(&m_data, std::bit_cast(old_value), static_cast(timeout), ext); } - void notify_one() noexcept + void notify_one() + requires(sizeof(type) == 4 || sizeof(type) == 8) { - atomic_wait_engine::notify_one(&m_data, sizeof(T), atomic_wait::default_mask); + atomic_wait_engine::notify_one(&m_data); } - // Notify with mask, allowing to not wake up thread which doesn't wait on this mask - void notify_one(type mask_value) noexcept + void notify_all() + requires(sizeof(type) == 4 || sizeof(type) == 8) { - const u128 mask = std::bit_cast>(mask_value); - atomic_wait_engine::notify_one(&m_data, sizeof(T), mask); - } - - void notify_all() noexcept - { - atomic_wait_engine::notify_all(&m_data, sizeof(T), atomic_wait::default_mask); - } - - // Notify all threads with mask, allowing to not wake up threads which don't wait on them - void notify_all(type mask_value) noexcept - { - const u128 mask = std::bit_cast>(mask_value); - atomic_wait_engine::notify_all(&m_data, sizeof(T), mask); + atomic_wait_engine::notify_all(&m_data); } }; @@ -1874,23 +1782,6 @@ public: { return base::fetch_xor(1) != 0; } - - // Timeout is discouraged - template - void wait(bool old_value, atomic_wait_timeout timeout = atomic_wait_timeout::inf) const noexcept - { - base::template wait(old_value, 1, timeout); - } - - void notify_one() noexcept - { - base::notify_one(1); - } - - void notify_all() noexcept - { - base::notify_all(1); - } }; // Specializations @@ -1904,12 +1795,6 @@ struct std::common_type, T2> : std::common_type struct std::common_type> : std::common_type, T2> {}; -namespace atomic_wait -{ - template - constexpr u128 default_mask> = 1; -} - #ifndef _MSC_VER #pragma GCC diagnostic pop #pragma GCC diagnostic pop diff --git a/rpcs3/util/fifo_mutex.hpp b/rpcs3/util/fifo_mutex.hpp index cf2d9b0d0e..cddb7b11cf 100644 --- a/rpcs3/util/fifo_mutex.hpp +++ b/rpcs3/util/fifo_mutex.hpp @@ -6,33 +6,35 @@ // Mutex that tries to maintain the order of acquisition class fifo_mutex { - // Low 8 bits are incremented on acquisition, high 8 bits are incremented on release - atomic_t m_value{0}; + // Low 16 bits are incremented on acquisition, high 16 bits are incremented on release + atomic_t m_value{0}; public: constexpr fifo_mutex() noexcept = default; void lock() noexcept { - const u16 val = m_value.fetch_op([](u16& val) + // clang-format off + const u32 val = m_value.fetch_op([](u32& val) { - val = (val & 0xff00) | ((val + 1) & 0xff); + val = (val & 0xffff0000) | ((val + 1) & 0xffff); }); + // clang-format on - if (val >> 8 != (val & 0xff)) [[unlikely]] + if (val >> 16 != (val & 0xffff)) [[unlikely]] { // TODO: implement busy waiting along with moving to cpp file - m_value.wait(((val + 1) & 0xff) << 8, 0xff00); + m_value.wait((val & 0xffff0000) | ((val + 1) & 0xffff)); } } bool try_lock() noexcept { - const u16 val = m_value.load(); + const u32 val = m_value.load(); - if (val >> 8 == (val & 0xff)) + if (val >> 16 == (val & 0xffff)) { - if (m_value.compare_and_swap(val, ((val + 1) & 0xff) | (val & 0xff00))) + if (m_value.compare_and_swap(val, ((val + 1) & 0xffff) | (val & 0xffff0000))) { return true; } @@ -43,9 +45,9 @@ public: void unlock() noexcept { - const u16 val = m_value.add_fetch(0x100); + const u32 val = m_value.add_fetch(0x10000); - if (val >> 8 != (val & 0xff)) + if (val >> 16 != (val & 0xffff)) { m_value.notify_one(); } @@ -53,9 +55,9 @@ public: bool is_free() const noexcept { - const u16 val = m_value.load(); + const u32 val = m_value.load(); - return (val >> 8) == (val & 0xff); + return (val >> 16) == (val & 0xffff); } void lock_unlock() noexcept diff --git a/rpcs3/util/media_utils.cpp b/rpcs3/util/media_utils.cpp index f99617f98f..83eacc0b51 100644 --- a/rpcs3/util/media_utils.cpp +++ b/rpcs3/util/media_utils.cpp @@ -259,8 +259,8 @@ namespace utils void audio_decoder::clear() { - track_fully_decoded = false; - track_fully_consumed = false; + track_fully_decoded = 0; + track_fully_consumed = 0; has_error = false; m_size = 0; duration_ms = 0; @@ -274,7 +274,7 @@ namespace utils { auto& thread = *m_thread; thread = thread_state::aborting; - track_fully_consumed = true; + track_fully_consumed = 1; track_fully_consumed.notify_one(); thread(); m_thread.reset(); @@ -511,7 +511,7 @@ namespace utils media_log.notice("audio_decoder: about to decode: %s (index=%d)", ::at32(m_context.playlist, m_context.current_track), m_context.current_track); decode_track(::at32(m_context.playlist, m_context.current_track)); - track_fully_decoded = true; + track_fully_decoded = 1; if (has_error) { @@ -521,7 +521,7 @@ namespace utils // Let's only decode one track at a time. Wait for the consumer to finish reading the track. media_log.notice("audio_decoder: waiting until track is consumed..."); - thread_ctrl::wait_on(track_fully_consumed, false); + thread_ctrl::wait_on(track_fully_consumed, 0); track_fully_consumed = false; } diff --git a/rpcs3/util/media_utils.h b/rpcs3/util/media_utils.h index 6c450a89d3..5c25d14be9 100644 --- a/rpcs3/util/media_utils.h +++ b/rpcs3/util/media_utils.h @@ -77,8 +77,8 @@ namespace utils std::vector data; atomic_t m_size = 0; atomic_t duration_ms = 0; - atomic_t track_fully_decoded{false}; - atomic_t track_fully_consumed{false}; + atomic_t track_fully_decoded{0}; + atomic_t track_fully_consumed{0}; atomic_t has_error{false}; std::deque> timestamps_ms; diff --git a/rpcs3/util/shared_ptr.hpp b/rpcs3/util/shared_ptr.hpp index 6e3e9dbfb9..23d4bfd974 100644 --- a/rpcs3/util/shared_ptr.hpp +++ b/rpcs3/util/shared_ptr.hpp @@ -3,6 +3,7 @@ #include #include #include "atomic.hpp" +#include "asm.hpp" namespace stx { @@ -21,7 +22,7 @@ namespace stx // Basic assumption of userspace pointer size constexpr uint c_ptr_size = 48; - // Use lower 17 bits as atomic_ptr internal counter of borrowed refs (pointer itself is shifted) + // Use lower 16 bits as atomic_ptr internal counter of borrowed refs (pointer itself is shifted) constexpr uint c_ref_mask = 0xffff, c_ref_size = 16; // Remaining pointer bits @@ -1054,20 +1055,19 @@ namespace stx return observe() == r.get(); } - template - void wait(const volatile void* value, atomic_wait_timeout timeout = atomic_wait_timeout::inf) + void wait(std::nullptr_t, atomic_wait_timeout timeout = atomic_wait_timeout::inf) { - m_val.wait(reinterpret_cast(value) << c_ref_size, c_ptr_mask, timeout); + utils::bless>(&m_val)[1].wait(0, timeout); } void notify_one() { - m_val.notify_one(c_ptr_mask); + utils::bless>(&m_val)[1].notify_one(); } void notify_all() { - m_val.notify_all(c_ptr_mask); + utils::bless>(&m_val)[1].notify_all(); } }; @@ -1110,18 +1110,6 @@ namespace stx } null_ptr; } -namespace atomic_wait -{ - template - constexpr u128 default_mask> = stx::c_ptr_mask; - - template - constexpr u128 get_value(stx::atomic_ptr&, const volatile void* value = nullptr) - { - return reinterpret_cast(value) << stx::c_ref_size; - } -} - using stx::null_ptr; using stx::single_ptr; using stx::shared_ptr; From 39a0ff99b28958acfc10468963e6367225517ceb Mon Sep 17 00:00:00 2001 From: Ivan Chikish Date: Thu, 3 Aug 2023 05:18:58 +0300 Subject: [PATCH 022/184] Fix lf_queue regression --- Utilities/lockless.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Utilities/lockless.h b/Utilities/lockless.h index ef19fa3f7e..7bd0133346 100644 --- a/Utilities/lockless.h +++ b/Utilities/lockless.h @@ -342,15 +342,14 @@ public: void push(Args&&... args) { auto oldv = m_head.load(); - auto _old = load(oldv); - auto item = new lf_queue_item(_old, std::forward(args)...); + auto item = new lf_queue_item(load(oldv), std::forward(args)...); while (!m_head.compare_exchange(oldv, reinterpret_cast(item) << 16)) { - item->m_link = _old; + item->m_link = load(oldv); } - if (!_old) + if (!oldv) { // Notify only if queue was empty utils::bless>(&m_head)[1].notify_one(); From 0f3dfec7f21964c34efa4dfc48a6a25b349a305a Mon Sep 17 00:00:00 2001 From: trigger Date: Mon, 31 Jul 2023 15:10:16 -0700 Subject: [PATCH 023/184] direct return in ppu_check --- rpcs3/Emu/Cell/PPUThread.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 8619dcd81e..a4fccecd75 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -2457,10 +2457,10 @@ static void ppu_check(ppu_thread& ppu, u64 addr) { ppu.cia = ::narrow(addr); - // ppu_check() shall not return directly if (ppu.test_stopped()) - {} - ppu_escape(&ppu); + { + return; + } } static void ppu_trace(u64 addr) From 804665df6910514d6091bb063d0250ee22d121e7 Mon Sep 17 00:00:00 2001 From: Ivan Chikish Date: Thu, 3 Aug 2023 13:51:44 +0300 Subject: [PATCH 024/184] Fixup futex_waitv redefinition --- Utilities/sync.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Utilities/sync.h b/Utilities/sync.h index 4e60c0f2b4..7264ba9248 100644 --- a/Utilities/sync.h +++ b/Utilities/sync.h @@ -51,8 +51,6 @@ constexpr NTSTATUS NTSTATUS_TIMEOUT = 0x102; #ifndef FUTEX_WAITV_MAX #define FUTEX_WAITV_MAX 128 -#endif - struct futex_waitv { __u64 val; @@ -60,6 +58,7 @@ struct futex_waitv __u32 flags; __u32 __reserved; }; +#endif #else enum { From 06c9b95e091d2b8a19c6a48851c4dc751f54cac5 Mon Sep 17 00:00:00 2001 From: Eladash Date: Tue, 1 Aug 2023 09:27:41 +0300 Subject: [PATCH 025/184] PPU LLVM/SPU/Non-TSX: Obnoxiously responsive and obedient PPU for SPU requests --- rpcs3/Emu/Cell/PPUTranslator.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rpcs3/Emu/Cell/PPUTranslator.cpp b/rpcs3/Emu/Cell/PPUTranslator.cpp index 83f0309f5d..f163111fb9 100644 --- a/rpcs3/Emu/Cell/PPUTranslator.cpp +++ b/rpcs3/Emu/Cell/PPUTranslator.cpp @@ -201,8 +201,12 @@ Function* PPUTranslator::Translate(const ppu_function& info) const auto vcheck = BasicBlock::Create(m_context, "__test", m_function); m_ir->CreateCondBr(m_ir->CreateIsNull(vstate), body, vcheck, m_md_likely); - // Create tail call to the check function m_ir->SetInsertPoint(vcheck); + + // Raise wait flag as soon as possible + m_ir->CreateAtomicRMW(llvm::AtomicRMWInst::Or, ptr, m_ir->getInt32((+cpu_flag::wait).operator u32()), llvm::MaybeAlign{4}, llvm::AtomicOrdering::AcquireRelease); + + // Create tail call to the check function Call(GetType(), "__check", m_thread, GetAddr())->setTailCall(); m_ir->CreateRetVoid(); } From 9f625de51a18422df903d79b2585c8fb50f2d5f1 Mon Sep 17 00:00:00 2001 From: Eladash Date: Thu, 3 Aug 2023 13:26:11 +0300 Subject: [PATCH 026/184] vm.cpp/Non-TSX: Fixup potential deadlock --- rpcs3/Emu/Memory/vm.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/rpcs3/Emu/Memory/vm.cpp b/rpcs3/Emu/Memory/vm.cpp index 0c2c719957..36e9c2e636 100644 --- a/rpcs3/Emu/Memory/vm.cpp +++ b/rpcs3/Emu/Memory/vm.cpp @@ -391,14 +391,19 @@ namespace vm return; } + if (!get_range_lock_bits(true)) [[likely]] + { + return; + } + if (i < 100) busy_wait(200); else std::this_thread::yield(); - if (!get_range_lock_bits(true)) [[likely]] + if (cpu_flag::wait - cpu.state) { - return; + cpu.state += cpu_flag::wait; } } } From 8057773c4f9fb82fca912e62c5892c8e4126df4d Mon Sep 17 00:00:00 2001 From: Eladash Date: Thu, 3 Aug 2023 09:20:09 +0300 Subject: [PATCH 027/184] Fix decrypt_binaries_t::done() --- rpcs3/Crypto/decrypt_binaries.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/Crypto/decrypt_binaries.h b/rpcs3/Crypto/decrypt_binaries.h index d9887b3d5f..be80c31d42 100644 --- a/rpcs3/Crypto/decrypt_binaries.h +++ b/rpcs3/Crypto/decrypt_binaries.h @@ -16,7 +16,7 @@ public: bool done() const { - return m_index >= m_klics.size(); + return m_index >= m_modules.size(); } const std::string& operator[](usz index) const From bb9215414514b24f7d71c19b61af41ed312009a1 Mon Sep 17 00:00:00 2001 From: Eladash Date: Thu, 3 Aug 2023 09:31:35 +0300 Subject: [PATCH 028/184] Auto-dump decrypted binaries if PPU debug is enabled --- rpcs3/Emu/System.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 22bf01dd26..eea2619c99 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -243,6 +243,39 @@ void fixup_ppu_settings() } } +void dump_executable(std::span data, main_ppu_module* _main, std::string_view title_id) +{ + // Format filename and directory name + // Make each directory for each file so tools like IDA can work on it cleanly + const std::string dir_path = fs::get_cache_dir() + "ppu_progs/" + std::string{!title_id.empty() ? title_id : "untitled"} + fmt::format("-%s-%s", fmt::base57(_main->sha1), _main->path.substr(_main->path.find_last_of('/') + 1)) + '/'; + const std::string filename = dir_path + "exec.elf"; + + if (fs::create_dir(dir_path) || fs::g_tls_error == fs::error::exist) + { + if (fs::file out{filename, fs::create + fs::write}) + { + if (out.size() == data.size()) + { + // Risky optimization: assume if file size match they are equal and does not need to rewrite it + // But it is a debug option and if there are problems the user/developer can remove the previous file + } + else + { + out.trunc(0); + out.write(data.data(), data.size()); + } + } + else + { + sys_log.error("Failed to save decrypted executable of \"%s\": Failure to create file \"%s\" (%s)", Emu.GetBoot(), filename, fs::g_tls_error); + } + } + else + { + sys_log.error("Failed to save decrypted executable of \"%s\": Failure to create directory \"%s\" (%s)", Emu.GetBoot(), dir_path, fs::g_tls_error); + } +} + void Emulator::Init() { jit_runtime::initialize(); @@ -492,6 +525,7 @@ void Emulator::Init() make_path_verbose(fs::get_cache_dir() + "shaderlog/", false); make_path_verbose(fs::get_cache_dir() + "spu_progs/", false); + make_path_verbose(fs::get_cache_dir() + "ppu_progs/", false); make_path_verbose(fs::get_parent_dir(get_savestate_file("NO_ID", "/NO_FILE", -1, -1)), false); make_path_verbose(fs::get_config_dir() + "captures/", false); make_path_verbose(fs::get_config_dir() + "sounds/", false); @@ -1884,10 +1918,13 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, return game_boot_result::invalid_file_or_folder; } + bool had_been_decrypted = false; + // Check SELF header if (elf_file.size() >= 4 && elf_file.read() == "SCE\0"_u32) { // Decrypt SELF + had_been_decrypted = true; elf_file = decrypt_self(std::move(elf_file), klic.empty() ? nullptr : reinterpret_cast(&klic[0]), &g_ps3_process_info.self_info); } else @@ -2007,10 +2044,18 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, sys_log.error("Booting HG category outside of HDD0!"); } - g_fxo->init(); + const auto _main = g_fxo->init(); if (ppu_load_exec(ppu_exec, false, m_path, DeserialManager())) { + if (g_cfg.core.ppu_debug && had_been_decrypted) + { + // Auto-dump decrypted binaries if PPU debug is enabled + + const auto exec_bin = elf_file.to_vector(); + + dump_executable({exec_bin.data(), exec_bin.size()}, _main, GetTitleID()); + } } // Overlay (OVL) executable (only load it) else if (vm::map(0x3000'0000, 0x1000'0000, 0x200); !ppu_load_overlay(ppu_exec, false, m_path).first) From 744a1528ccb98896f8e2b46226c770b76b5f9eaa Mon Sep 17 00:00:00 2001 From: Eladash Date: Thu, 3 Aug 2023 11:15:34 +0300 Subject: [PATCH 029/184] Optimize memory usage of ELF loader Do not duplicate shdr memory when it is present in phdr. --- rpcs3/Emu/Cell/PPUModule.cpp | 2 +- rpcs3/Emu/Cell/RawSPUThread.cpp | 2 +- rpcs3/Loader/ELF.h | 76 +++++++++++++++++++++++++++++++-- 3 files changed, 74 insertions(+), 6 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index f311763a42..8ac6832c9d 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -2783,7 +2783,7 @@ bool ppu_load_rel_exec(const ppu_rel_object& elf) _sec.addr = addr; relm.secs.emplace_back(_sec); - std::memcpy(vm::base(addr), s.bin.data(), size); + std::memcpy(vm::base(addr), s.get_bin().data(), size); addr = utils::align(addr + size, 128); } } diff --git a/rpcs3/Emu/Cell/RawSPUThread.cpp b/rpcs3/Emu/Cell/RawSPUThread.cpp index eaf5e2dbc1..f442e73f89 100644 --- a/rpcs3/Emu/Cell/RawSPUThread.cpp +++ b/rpcs3/Emu/Cell/RawSPUThread.cpp @@ -410,7 +410,7 @@ void spu_load_rel_exec(const spu_rel_object& elf) { if (shdr.sh_type == sec_type::sht_progbits && shdr.sh_flags().all_of(sh_flag::shf_alloc)) { - std::memcpy(spu->_ptr(offs), shdr.bin.data(), shdr.sh_size); + std::memcpy(spu->_ptr(offs), shdr.get_bin().data(), shdr.sh_size); offs = utils::align(offs + shdr.sh_size, 4); } } diff --git a/rpcs3/Loader/ELF.h b/rpcs3/Loader/ELF.h index c7340b7115..831e58dea1 100644 --- a/rpcs3/Loader/ELF.h +++ b/rpcs3/Loader/ELF.h @@ -4,6 +4,8 @@ #include "../../Utilities/File.h" #include "../../Utilities/bit_set.h" +#include + enum class elf_os : u8 { none = 0, @@ -191,10 +193,21 @@ template class en_t, typename sz_t> struct elf_shdata final : elf_shdr { std::vector bin{}; + std::span bin_view{}; using base = elf_shdr; elf_shdata() = default; + + std::span get_bin() const + { + if (!bin_view.empty()) + { + return bin_view; + } + + return {bin.data(), bin.size()}; + } }; // ELF loading options @@ -340,6 +353,26 @@ public: if (!(opts & elf_opt::no_data) && is_memorizable_section(shdr.sh_type, shdr.sh_flags())) { + usz p_index = umax; + + for (const auto& hdr : _phdrs) + { + // Try to find it in phdr data instead of allocating new section + p_index++; + + if (hdr.p_offset <= shdr.sh_offset && shdr.sh_offset + shdr.sh_size - 1 <= hdr.p_offset + hdr.p_filesz - 1) + { + const auto& prog = ::at32(progs, p_index); + shdrs.back().bin_view = {prog.bin.data() + shdr.sh_offset - hdr.p_offset, shdr.sh_size}; + } + } + + if (!shdrs.back().bin_view.empty()) + { + // Optimized + continue; + } + stream.seek(offset + shdr.sh_offset); if (!stream.read(shdrs.back().bin, shdr.sh_size)) return set_error(elf_error::stream_data); @@ -400,14 +433,49 @@ public: stream.write(shdr_t{}); } - for (shdr_t shdr : shdrs) + for (const auto& shdr : shdrs) { + shdr_t out = static_cast(shdr); + if (is_memorizable_section(shdr.sh_type, shdr.sh_flags())) { - shdr.sh_offset = std::exchange(off, off + shdr.sh_size); + usz p_index = umax; + usz data_base = header.e_shoff + u32{sizeof(shdr_t)} * ::size32(shdrs); + bool result = false; + + for (const auto& hdr : progs) + { + if (shdr.bin_view.empty()) + { + break; + } + + // Try to find it in phdr data instead of writing new section + p_index++; + + // Rely on previous sh_offset value! + if (hdr.p_offset <= shdr.sh_offset && shdr.sh_offset + shdr.sh_size - 1 <= hdr.p_offset + hdr.p_filesz - 1) + { + const auto& prog = ::at32(progs, p_index); + out.sh_offset = data_base + shdr.sh_offset - hdr.p_offset; + result = true; + break; + } + + data_base += ::at32(progs, p_index).p_filesz; + } + + if (result) + { + // Optimized + } + else + { + out.sh_offset = std::exchange(off, off + shdr.sh_size); + } } - stream.write(shdr); + stream.write(static_cast(out)); } // Write data @@ -418,7 +486,7 @@ public: for (const auto& shdr : shdrs) { - if (!is_memorizable_section(shdr.sh_type, shdr.sh_flags())) + if (!is_memorizable_section(shdr.sh_type, shdr.sh_flags()) || !shdr.bin_view.empty()) { continue; } From 65685d4525c85b1a3ab930237be37478e9d28b9b Mon Sep 17 00:00:00 2001 From: MSuih Date: Fri, 4 Aug 2023 10:51:39 +0300 Subject: [PATCH 030/184] Fix current date format --- rpcs3/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/main.cpp b/rpcs3/main.cpp index 7609c7898d..5354af4e69 100644 --- a/rpcs3/main.cpp +++ b/rpcs3/main.cpp @@ -422,7 +422,7 @@ template <> void fmt_class_string>::format(std::string& out, u64 arg) { const std::time_t dateTime = std::chrono::system_clock::to_time_t(get_object(arg)); - out += date_time::fmt_time("%Y-%m-%eT%H:%M:%S", dateTime); + out += date_time::fmt_time("%Y-%m-%dT%H:%M:%S", dateTime); } void run_platform_sanity_checks() From 26ecd88074d1d28564a8a74fa659f4eed4a1eac2 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Fri, 4 Aug 2023 01:23:17 +0300 Subject: [PATCH 031/184] rsx: Rebuild shader texture state if we detect a silent mismatch --- rpcs3/Emu/RSX/GL/GLDraw.cpp | 36 ++++++++++++++++++++++++++---------- rpcs3/Emu/RSX/VK/VKDraw.cpp | 23 ++++++++++++++++++++++- 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/rpcs3/Emu/RSX/GL/GLDraw.cpp b/rpcs3/Emu/RSX/GL/GLDraw.cpp index 360c252a83..6eba64a9d6 100644 --- a/rpcs3/Emu/RSX/GL/GLDraw.cpp +++ b/rpcs3/Emu/RSX/GL/GLDraw.cpp @@ -341,23 +341,31 @@ void GLGSRender::load_texture_env() auto sampler_state = static_cast(fs_sampler_state[i].get()); const auto& tex = rsx::method_registers.fragment_textures[i]; + const auto previous_format_class = sampler_state->format_class; if (m_samplers_dirty || m_textures_dirty[i] || m_gl_texture_cache.test_if_descriptor_expired(cmd, m_rtts, sampler_state, tex)) { if (tex.enabled()) { *sampler_state = m_gl_texture_cache.upload_texture(cmd, tex, m_rtts); + + if (sampler_state->validate()) + { + if (m_textures_dirty[i]) + { + m_fs_sampler_states[i].apply(tex, fs_sampler_state[i].get()); + } + else if (sampler_state->format_class != previous_format_class) + { + m_graphics_state |= rsx::fragment_program_state_dirty; + } + } } else { *sampler_state = {}; } - if (m_textures_dirty[i] && sampler_state->validate()) - { - m_fs_sampler_states[i].apply(tex, fs_sampler_state[i].get()); - } - m_textures_dirty[i] = false; } } @@ -372,23 +380,31 @@ void GLGSRender::load_texture_env() auto sampler_state = static_cast(vs_sampler_state[i].get()); const auto& tex = rsx::method_registers.vertex_textures[i]; + const auto previous_format_class = sampler_state->format_class; if (m_samplers_dirty || m_vertex_textures_dirty[i] || m_gl_texture_cache.test_if_descriptor_expired(cmd, m_rtts, sampler_state, tex)) { if (rsx::method_registers.vertex_textures[i].enabled()) { *sampler_state = m_gl_texture_cache.upload_texture(cmd, rsx::method_registers.vertex_textures[i], m_rtts); + + if (sampler_state->validate()) + { + if (m_vertex_textures_dirty[i]) + { + m_vs_sampler_states[i].apply(tex, vs_sampler_state[i].get()); + } + else if (sampler_state->format_class != previous_format_class) + { + m_graphics_state |= rsx::vertex_program_state_dirty; + } + } } else { *sampler_state = {}; } - if (m_vertex_textures_dirty[i] && sampler_state->validate()) - { - m_vs_sampler_states[i].apply(tex, vs_sampler_state[i].get()); - } - m_vertex_textures_dirty[i] = false; } } diff --git a/rpcs3/Emu/RSX/VK/VKDraw.cpp b/rpcs3/Emu/RSX/VK/VKDraw.cpp index 3b9c11fb19..82498ba91a 100644 --- a/rpcs3/Emu/RSX/VK/VKDraw.cpp +++ b/rpcs3/Emu/RSX/VK/VKDraw.cpp @@ -256,6 +256,7 @@ void VKGSRender::load_texture_env() auto sampler_state = static_cast(fs_sampler_state[i].get()); const auto& tex = rsx::method_registers.fragment_textures[i]; + const auto previous_format_class = fs_sampler_state[i]->format_class; if (m_samplers_dirty || m_textures_dirty[i] || !check_surface_cache_sampler(sampler_state, tex)) { @@ -276,6 +277,12 @@ void VKGSRender::load_texture_env() check_for_cyclic_refs |= true; } + if (!m_textures_dirty[i] && sampler_state->format_class != previous_format_class) + { + // Host details changed but RSX is not aware + m_graphics_state |= rsx::fragment_program_state_dirty; + } + bool replace = !fs_sampler_handles[i]; VkFilter mag_filter; vk::minification_filter min_filter; @@ -403,6 +410,7 @@ void VKGSRender::load_texture_env() auto sampler_state = static_cast(vs_sampler_state[i].get()); const auto& tex = rsx::method_registers.vertex_textures[i]; + const auto previous_format_class = sampler_state->format_class; if (m_samplers_dirty || m_vertex_textures_dirty[i] || !check_surface_cache_sampler(sampler_state, tex)) { @@ -423,6 +431,12 @@ void VKGSRender::load_texture_env() check_for_cyclic_refs |= true; } + if (!m_vertex_textures_dirty[i] && sampler_state->format_class != previous_format_class) + { + // Host details changed but RSX is not aware + m_graphics_state |= rsx::vertex_program_state_dirty; + } + bool replace = !vs_sampler_handles[i]; const VkBool32 unnormalized_coords = !!(tex.format() & CELL_GCM_TEXTURE_UN); const auto min_lod = tex.min_lod(); @@ -1015,10 +1029,17 @@ void VKGSRender::end() // Now bind the shader resources. It is important that this takes place after the barriers so that we don't end up with stale descriptors for (int retry = 0; retry < 3; ++retry) { - if (m_samplers_dirty) [[ unlikely ]] + if (retry > 0 && m_samplers_dirty) [[ unlikely ]] { // Reload texture env if referenced objects were invalidated during OOM handling. load_texture_env(); + + // Do not trust fragment/vertex texture state after a texture state reset. + // NOTE: We don't want to change the program - it's too late for that now. We just need to harmonize the state. + m_graphics_state |= rsx::vertex_program_state_dirty | rsx::fragment_program_state_dirty; + get_current_fragment_program(fs_sampler_state); + get_current_vertex_program(vs_sampler_state); + m_graphics_state.clear(rsx::pipeline_state::invalidate_pipeline_bits); } const bool out_of_memory = m_shader_interpreter.is_interpreter(m_program) From 51a8d2235c22090b282f411f4860759d24bec95b Mon Sep 17 00:00:00 2001 From: trigger Date: Mon, 31 Jul 2023 22:15:07 -0700 Subject: [PATCH 032/184] osk: ignore input based on ignore_device_events --- rpcs3/Emu/RSX/Overlays/overlay_osk.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp b/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp index 5bebaa226e..f4115720ba 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp @@ -644,10 +644,10 @@ namespace rsx void osk_dialog::on_button_pressed(pad_button button_press, bool is_auto_repeat) { - if (!pad_input_enabled) + if (!pad_input_enabled || ignore_device_events) return; - if (!ignore_device_events && input_device.exchange(CELL_OSKDIALOG_INPUT_DEVICE_PAD) != CELL_OSKDIALOG_INPUT_DEVICE_PAD) + if (input_device.exchange(CELL_OSKDIALOG_INPUT_DEVICE_PAD) != CELL_OSKDIALOG_INPUT_DEVICE_PAD) { osk.notice("on_button_pressed: sending CELL_SYSUTIL_OSKDIALOG_INPUT_DEVICE_CHANGED with CELL_OSKDIALOG_INPUT_DEVICE_PAD"); sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_INPUT_DEVICE_CHANGED, CELL_OSKDIALOG_INPUT_DEVICE_PAD); @@ -900,10 +900,10 @@ namespace rsx void osk_dialog::on_key_pressed(u32 led, u32 mkey, u32 key_code, u32 out_key_code, bool pressed, std::u32string key) { - if (!pressed || !keyboard_input_enabled) + if (!pressed || !keyboard_input_enabled || ignore_device_events) return; - if (!ignore_device_events && input_device.exchange(CELL_OSKDIALOG_INPUT_DEVICE_KEYBOARD) != CELL_OSKDIALOG_INPUT_DEVICE_KEYBOARD) + if (input_device.exchange(CELL_OSKDIALOG_INPUT_DEVICE_KEYBOARD) != CELL_OSKDIALOG_INPUT_DEVICE_KEYBOARD) { osk.notice("on_key_pressed: sending CELL_SYSUTIL_OSKDIALOG_INPUT_DEVICE_CHANGED with CELL_OSKDIALOG_INPUT_DEVICE_KEYBOARD"); sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_INPUT_DEVICE_CHANGED, CELL_OSKDIALOG_INPUT_DEVICE_KEYBOARD); From 0a673129ac8942f4dbe2f2d2a1f50a8e47a2bc68 Mon Sep 17 00:00:00 2001 From: nastys <7950891+nastys@users.noreply.github.com> Date: Fri, 4 Aug 2023 22:01:30 +0200 Subject: [PATCH 033/184] macOS CI: build faudio from source --- .ci/build-mac.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.ci/build-mac.sh b/.ci/build-mac.sh index 6276fe92d9..eac07f67c0 100755 --- a/.ci/build-mac.sh +++ b/.ci/build-mac.sh @@ -6,7 +6,8 @@ brew install -f --overwrite nasm ninja git p7zip create-dmg ccache pipenv #/usr/sbin/softwareupdate --install-rosetta --agree-to-license arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" arch -x86_64 /usr/local/bin/brew update -arch -x86_64 /usr/local/bin/brew install -f --overwrite llvm@16 sdl2 glew cmake faudio vulkan-headers ffmpeg +arch -x86_64 /usr/local/bin/brew install -f --overwrite llvm@16 sdl2 glew cmake vulkan-headers ffmpeg +arch -x86_64 /usr/local/bin/brew install -f --overwrite --build-from-source faudio arch -x86_64 /usr/local/bin/brew link -f llvm@16 # moltenvk based on commit for 1.2.4 release From 96d94d6217489b5fa22e4e64b2b7b94538b6d1dc Mon Sep 17 00:00:00 2001 From: nastys <7950891+nastys@users.noreply.github.com> Date: Fri, 4 Aug 2023 23:06:26 +0200 Subject: [PATCH 034/184] macOS CI: use faudio from submodule --- .ci/build-mac.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.ci/build-mac.sh b/.ci/build-mac.sh index eac07f67c0..0a94540537 100755 --- a/.ci/build-mac.sh +++ b/.ci/build-mac.sh @@ -6,8 +6,7 @@ brew install -f --overwrite nasm ninja git p7zip create-dmg ccache pipenv #/usr/sbin/softwareupdate --install-rosetta --agree-to-license arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" arch -x86_64 /usr/local/bin/brew update -arch -x86_64 /usr/local/bin/brew install -f --overwrite llvm@16 sdl2 glew cmake vulkan-headers ffmpeg -arch -x86_64 /usr/local/bin/brew install -f --overwrite --build-from-source faudio +arch -x86_64 /usr/local/bin/brew install -f --overwrite llvm@16 glew cmake sdl2 vulkan-headers ffmpeg arch -x86_64 /usr/local/bin/brew link -f llvm@16 # moltenvk based on commit for 1.2.4 release @@ -61,7 +60,7 @@ export LLVM_DIR LLVM_DIR="BREW_X64_PATH/opt/llvm@16" # exclude FAudio, SPIRV and LLVM, and sdl from submodule update # shellcheck disable=SC2046 -git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/FAudio/ && !/llvm/ && !/SPIRV/ && !/SDL/ { print $3 }' .gitmodules) +git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/llvm/ && !/SPIRV/ && !/SDL/ { print $3 }' .gitmodules) # 3rdparty fixes sed -i '' "s/extern const double NSAppKitVersionNumber;/const double NSAppKitVersionNumber = 1343;/g" 3rdparty/hidapi/hidapi/mac/hid.c @@ -88,7 +87,7 @@ mkdir build && cd build || exit 1 -DLLVM_ENABLE_Z3_SOLVER=OFF \ -DUSE_NATIVE_INSTRUCTIONS=OFF \ -DUSE_SYSTEM_MVK=ON \ - -DUSE_SYSTEM_FAUDIO=ON \ + -DUSE_SYSTEM_FAUDIO=OFF \ -DUSE_SYSTEM_SDL=ON \ $CMAKE_EXTRA_OPTS \ -DLLVM_TARGET_ARCH=X86_64 \ From aee97e414fb78aedd396b04b296f5d54361220b4 Mon Sep 17 00:00:00 2001 From: nastys <7950891+nastys@users.noreply.github.com> Date: Fri, 4 Aug 2023 22:18:40 +0200 Subject: [PATCH 035/184] macOS CI: use lzma compression for the DMG --- .ci/deploy-mac.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.ci/deploy-mac.sh b/.ci/deploy-mac.sh index 08697c3c8b..8477ae6fac 100755 --- a/.ci/deploy-mac.sh +++ b/.ci/deploy-mac.sh @@ -45,6 +45,7 @@ DMG_FILEPATH="$BUILD_ARTIFACTSTAGINGDIRECTORY/rpcs3-v${COMM_TAG}-${COMM_COUNT}-$ --hide-extension Quickstart.url \ --app-drop-link 600 185 \ --skip-jenkins \ +--format ULMO \ "$DMG_FILEPATH" \ RPCS3.app From d4cf12bc17d1eb0c59a356f17034f0e892b7a201 Mon Sep 17 00:00:00 2001 From: Whatcookie Date: Sat, 5 Aug 2023 04:49:30 -0400 Subject: [PATCH 036/184] LV2: Improve sys_timer_usleep by using CPU usermode waiting * Linux: set timerslack to minimum value - Linux delays the wakeup of threads to save power, this feature isn't needed for this application * Utils: Add detection for waitpkg and monitorx extensions - These instructions are used for user mode wait instructions * lv2: Use user mode wait instructions instead of yielding when appropriate --- rpcs3/Emu/Cell/lv2/lv2.cpp | 56 ++++++++++++++++++++++++++++++++++++++ rpcs3/main.cpp | 5 ++++ rpcs3/util/sysinfo.cpp | 35 +++++++++++++++++++++++- rpcs3/util/sysinfo.hpp | 6 ++++ 4 files changed, 101 insertions(+), 1 deletion(-) diff --git a/rpcs3/Emu/Cell/lv2/lv2.cpp b/rpcs3/Emu/Cell/lv2/lv2.cpp index 9b2473ec2b..b265c83dfe 100644 --- a/rpcs3/Emu/Cell/lv2/lv2.cpp +++ b/rpcs3/Emu/Cell/lv2/lv2.cpp @@ -53,6 +53,17 @@ #include #include #include "util/tsc.hpp" +#include "util/sysinfo.hpp" + +#if defined(ARCH_X64) +#ifdef _MSC_VER +#include +#include +#else +#include +#endif +#endif + extern std::string ppu_get_syscall_name(u64 code); @@ -1880,6 +1891,35 @@ void lv2_obj::set_yield_frequency(u64 freq, u64 max_allowed_tsc) g_lv2_preempts_taken.release(0); } +#if defined(_MSC_VER) +#define mwaitx_func +#define waitpkg_func +#else +#define mwaitx_func __attribute__((__target__("mwaitx"))) +#define waitpkg_func __attribute__((__target__("waitpkg"))) +#endif + +#if defined(ARCH_X64) +// Waits for a number of TSC clock cycles in power optimized state +// Cstate is represented in bits [7:4]+1 cstate. So C0 requires bits [7:4] to be set to 0xf, C1 requires bits [7:4] to be set to 0. +mwaitx_func static void __mwaitx(u32 cycles, u32 cstate) +{ + constexpr u32 timer_enable = 0x2; + + // monitorx will wake if the cache line is written to. We don't want this, so place the monitor value on it's own cache line. + alignas(64) u64 monitor_var{}; + _mm_monitorx(&monitor_var, 0, 0); + _mm_mwaitx(timer_enable, cstate, cycles); +} + +// First bit indicates cstate, 0x0 for C.02 state (lower power) or 0x1 for C.01 state (higher power) +waitpkg_func static void __tpause(u32 cycles, u32 cstate) +{ + const u64 tsc = utils::get_tsc() + cycles; + _tpause(cstate, tsc); +} +#endif + bool lv2_obj::wait_timeout(u64 usec, ppu_thread* cpu, bool scale, bool is_usleep) { static_assert(u64{umax} / max_timeout >= 100, "max timeout is not valid for scaling"); @@ -1965,6 +2005,7 @@ bool lv2_obj::wait_timeout(u64 usec, ppu_thread* cpu, bool scale, bool is_usleep if (remaining > host_min_quantum) { #ifdef __linux__ + // With timerslack set low, Linux is precise for all values above wait_for(remaining); #else // Wait on multiple of min quantum for large durations to avoid overloading low thread cpus @@ -1972,6 +2013,21 @@ bool lv2_obj::wait_timeout(u64 usec, ppu_thread* cpu, bool scale, bool is_usleep #endif } // TODO: Determine best value for yield delay +#if defined(ARCH_X64) + else if (utils::has_appropriate_um_wait()) + { + u32 us_in_tsc_clocks = remaining * (utils::get_tsc_freq() / 1000000); + + if (utils::has_waitpkg()) + { + __tpause(us_in_tsc_clocks, 0x1); + } + else + { + __mwaitx(us_in_tsc_clocks, 0xf0); + } + } +#endif else { // Try yielding. May cause long wake latency but helps weaker CPUs a lot by alleviating resource pressure diff --git a/rpcs3/main.cpp b/rpcs3/main.cpp index 5354af4e69..3dda91b80a 100644 --- a/rpcs3/main.cpp +++ b/rpcs3/main.cpp @@ -1049,6 +1049,11 @@ int main(int argc, char** argv) } } +// Set timerslack value for Linux. The default value is 50,000ns. Change this to just 1 since we value precise timers. +#ifdef __linux__ + prctl(PR_SET_TIMERSLACK, 1, 0, 0, 0); +#endif + #ifdef _WIN32 // Create dummy permanent low resolution timer to workaround messing with system timer resolution QTimer* dummy_timer = new QTimer(app.data()); diff --git a/rpcs3/util/sysinfo.cpp b/rpcs3/util/sysinfo.cpp index af44d1df50..24a264a32f 100755 --- a/rpcs3/util/sysinfo.cpp +++ b/rpcs3/util/sysinfo.cpp @@ -298,7 +298,7 @@ bool utils::has_fma4() bool utils::has_fast_vperm2b() { #if defined(ARCH_X64) - static const bool g_value = has_avx512() && (get_cpuid(7, 0)[2] & 0x2) == 0x2 && get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(0x80000001, 0)[2] & 0x20) == 0x20; + static const bool g_value = has_avx512() && (get_cpuid(7, 0)[2] & 0x2) == 0x2 && get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(0x80000001, 0)[2] & 0x40) == 0x40; return g_value; #else return false; @@ -325,6 +325,39 @@ bool utils::has_fsrm() #endif } +bool utils::has_waitx() +{ +#if defined(ARCH_X64) + static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(0x80000001, 0)[2] & 0x20000000) == 0x20000000; + return g_value; +#else + return false; +#endif +} + +bool utils::has_waitpkg() +{ +#if defined(ARCH_X64) + static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(7, 0)[2] & 0x20) == 0x20; + return g_value; +#else + return false; +#endif +} + +// User mode waits may be unfriendly to low thread CPUs +// Filter out systems with less than 8 threads for linux and less than 12 threads for other platforms +bool utils::has_appropriate_um_wait() +{ +#ifdef __linux__ + static const bool g_value = (has_waitx() || has_waitpkg()) && (get_thread_count() >= 8) && get_tsc_freq(); + return g_value; +#else + static const bool g_value = (has_waitx() || has_waitpkg()) && (get_thread_count() >= 12) && get_tsc_freq(); + return g_value; +#endif +} + u32 utils::get_rep_movsb_threshold() { static const u32 g_value = []() diff --git a/rpcs3/util/sysinfo.hpp b/rpcs3/util/sysinfo.hpp index 39826af5a8..2ce46b7c6c 100755 --- a/rpcs3/util/sysinfo.hpp +++ b/rpcs3/util/sysinfo.hpp @@ -53,6 +53,12 @@ namespace utils bool has_fsrm(); + bool has_waitx(); + + bool has_waitpkg(); + + bool has_appropriate_um_wait(); + std::string get_cpu_brand(); std::string get_system_info(); From 7e4cb20ac33677fa9ce35ab5861a46028999af57 Mon Sep 17 00:00:00 2001 From: Margen67 Date: Wed, 2 Aug 2023 02:30:00 -0700 Subject: [PATCH 037/184] CI: Don't silence curl errors -f: https://curl.se/docs/manpage.html#-f Only redirect stdout so stderr isn't lost. (stdin isn't needed?) --- .ci/deploy-linux.sh | 6 +++--- .ci/deploy-windows.sh | 4 ++-- .ci/get_keys-windows.sh | 4 ++-- .ci/github-upload.sh | 4 ++-- .ci/setup-windows.sh | 8 ++++---- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.ci/deploy-linux.sh b/.ci/deploy-linux.sh index fe50a81312..f43b0b021a 100755 --- a/.ci/deploy-linux.sh +++ b/.ci/deploy-linux.sh @@ -5,11 +5,11 @@ cd build || exit 1 if [ "$DEPLOY_APPIMAGE" = "true" ]; then DESTDIR=AppDir ninja install - curl -sL -o /usr/bin/linuxdeploy https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage + curl -fsSLo /usr/bin/linuxdeploy https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage chmod +x /usr/bin/linuxdeploy - curl -sL -o /usr/bin/linuxdeploy-plugin-qt https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage + curl -fsSLo /usr/bin/linuxdeploy-plugin-qt https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage chmod +x /usr/bin/linuxdeploy-plugin-qt - curl -sL -o linuxdeploy-plugin-checkrt.sh https://github.com/linuxdeploy/linuxdeploy-plugin-checkrt/releases/download/continuous/linuxdeploy-plugin-checkrt-x86_64.sh + curl -fsSLo linuxdeploy-plugin-checkrt.sh https://github.com/linuxdeploy/linuxdeploy-plugin-checkrt/releases/download/continuous/linuxdeploy-plugin-checkrt-x86_64.sh chmod +x ./linuxdeploy-plugin-checkrt.sh EXTRA_QT_PLUGINS="svg;" APPIMAGE_EXTRACT_AND_RUN=1 linuxdeploy --appdir AppDir --plugin qt diff --git a/.ci/deploy-windows.sh b/.ci/deploy-windows.sh index 48bd36661d..417e8e8a1b 100755 --- a/.ci/deploy-windows.sh +++ b/.ci/deploy-windows.sh @@ -9,8 +9,8 @@ rm -rf ./bin/git # Prepare compatibility database for packaging, as well as # certificate for ssl (auto-updater) -curl -sL 'https://rpcs3.net/compatibility?api=v1&export' | iconv -t UTF-8 > ./bin/GuiConfigs/compat_database.dat -curl -sL 'https://curl.haxx.se/ca/cacert.pem' > ./bin/cacert.pem +curl -fsSL 'https://rpcs3.net/compatibility?api=v1&export' | iconv -t UTF-8 1> ./bin/GuiConfigs/compat_database.dat +curl -fsSL 'https://curl.haxx.se/ca/cacert.pem' 1> ./bin/cacert.pem # Package artifacts 7z a -m0=LZMA2 -mx9 "$BUILD" ./bin/* diff --git a/.ci/get_keys-windows.sh b/.ci/get_keys-windows.sh index a6201f54a3..9ef56dda62 100644 --- a/.ci/get_keys-windows.sh +++ b/.ci/get_keys-windows.sh @@ -1,4 +1,4 @@ #!/bin/sh -ex -curl -L -o "./llvm.lock" "https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-16.0.1/llvmlibs_mt.7z.sha256" -curl -L -o "./glslang.lock" "https://github.com/RPCS3/glslang/releases/download/custom-build-win/glslanglibs_mt.7z.sha256" +curl -fLo "./llvm.lock" "https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-16.0.1/llvmlibs_mt.7z.sha256" +curl -fLo "./glslang.lock" "https://github.com/RPCS3/glslang/releases/download/custom-build-win/glslanglibs_mt.7z.sha256" diff --git a/.ci/github-upload.sh b/.ci/github-upload.sh index 53d3d09895..8cc89314d5 100755 --- a/.ci/github-upload.sh +++ b/.ci/github-upload.sh @@ -16,7 +16,7 @@ generate_post_data() EOF } -curl -s \ +curl -fsS \ -H "Authorization: token ${RPCS3_TOKEN}" \ -H "Accept: application/vnd.github.v3+json" \ --data "$(generate_post_data)" "https://api.github.com/repos/$UPLOAD_REPO_FULL_NAME/releases" >> release.json @@ -28,7 +28,7 @@ echo "${id:?}" upload_file() { - curl -s \ + curl -fsS \ -H "Authorization: token ${RPCS3_TOKEN}" \ -H "Accept: application/vnd.github.v3+json" \ -H "Content-Type: application/octet-stream" \ diff --git a/.ci/setup-windows.sh b/.ci/setup-windows.sh index d3bc02289b..616dabbf31 100755 --- a/.ci/setup-windows.sh +++ b/.ci/setup-windows.sh @@ -60,7 +60,7 @@ download_and_verify() fileName="$4" for _ in 1 2 3; do - [ -e "$CACHE_DIR/$fileName" ] || curl -L -o "$CACHE_DIR/$fileName" "$url" + [ -e "$CACHE_DIR/$fileName" ] || curl -fLo "$CACHE_DIR/$fileName" "$url" fileChecksum=$("${algo}sum" "$CACHE_DIR/$fileName" | awk '{ print $1 }') [ "$fileChecksum" = "$correctChecksum" ] && return 0 rm "$CACHE_DIR/$fileName" @@ -79,9 +79,9 @@ for url in $DEP_URLS; do # shellcheck disable=SC1003 case "$url" in - *qt*) checksum=$(curl -L "${url}.sha1"); algo="sha1"; outDir='C:\Qt\' ;; - *llvm*) checksum=$(curl -L "${url}.sha256"); algo="sha256"; outDir="./3rdparty/llvm" ;; - *glslang*) checksum=$(curl -L "${url}.sha256"); algo="sha256"; outDir="./lib/Release-x64" ;; + *qt*) checksum=$(curl -fL "${url}.sha1"); algo="sha1"; outDir='C:\Qt\' ;; + *llvm*) checksum=$(curl -fL "${url}.sha256"); algo="sha256"; outDir="./3rdparty/llvm" ;; + *glslang*) checksum=$(curl -fL "${url}.sha256"); algo="sha256"; outDir="./lib/Release-x64" ;; *Vulkan*) # Vulkan setup needs to be run in batch environment # Need to subshell this or else it doesn't wait From dea24c905babd3dd2063ce75a4bab4bf07333c80 Mon Sep 17 00:00:00 2001 From: nastys Date: Sat, 5 Aug 2023 11:19:52 +0200 Subject: [PATCH 038/184] Make experimental build warning modal --- rpcs3/rpcs3qt/gui_application.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/rpcs3/rpcs3qt/gui_application.cpp b/rpcs3/rpcs3qt/gui_application.cpp index 4ea300cc29..982e1632ed 100644 --- a/rpcs3/rpcs3qt/gui_application.cpp +++ b/rpcs3/rpcs3qt/gui_application.cpp @@ -73,6 +73,7 @@ bool gui_application::Init() gui_log.warning("Experimental Build Warning! Build origin: %s", branch_name); QMessageBox msg; + msg.setWindowModality(Qt::WindowModal); msg.setWindowTitle(tr("Experimental Build Warning")); msg.setIcon(QMessageBox::Critical); msg.setTextFormat(Qt::RichText); From 6672499ddee6aff04032509d667386ae94f1d31c Mon Sep 17 00:00:00 2001 From: Ani Date: Sat, 5 Aug 2023 22:49:32 +0100 Subject: [PATCH 039/184] vk: Fix detection of RADV on get_driver_vendor() Since Mesa 22.2.0 (2022-09-21), commit https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/11027/diffs?commit_id=f06da59fd75d7ce7708d159753fcdbc11de16f9e, the deviceName property has included the name of the GPU, thus invalidating our previous method of detecting RADV as a driver vendor Before: "AMD RADV NAVY_FLOUNDER" After: "AMD Radeon RX 6700M (RADV NAVI22)" Before: "AMD RADV RENOIR" After: "AMD Radeon Graphics (RADV RENOIR)" --- rpcs3/Emu/RSX/VK/vkutils/device.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rpcs3/Emu/RSX/VK/vkutils/device.cpp b/rpcs3/Emu/RSX/VK/vkutils/device.cpp index f2f1673d0c..885060b3e2 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/device.cpp +++ b/rpcs3/Emu/RSX/VK/vkutils/device.cpp @@ -255,6 +255,11 @@ namespace vk { const auto gpu_name = get_name(); + if (gpu_name.find("RADV") != umax) + { + return driver_vendor::RADV; + } + if (gpu_name.find("Radeon") != umax) { return driver_vendor::AMD; @@ -265,11 +270,6 @@ namespace vk return driver_vendor::NVIDIA; } - if (gpu_name.find("RADV") != umax) - { - return driver_vendor::RADV; - } - if (gpu_name.find("Intel") != umax) { #ifdef _WIN32 From e25936c1f1b16e23bb2c4bf1846f17d8b02eb650 Mon Sep 17 00:00:00 2001 From: Eladash Date: Fri, 4 Aug 2023 20:28:41 +0300 Subject: [PATCH 040/184] PPU LLVM/RawSPU: Fixup MMIO crossing-out --- rpcs3/Emu/Cell/PPUTranslator.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUTranslator.cpp b/rpcs3/Emu/Cell/PPUTranslator.cpp index f163111fb9..e591a5fada 100644 --- a/rpcs3/Emu/Cell/PPUTranslator.cpp +++ b/rpcs3/Emu/Cell/PPUTranslator.cpp @@ -3617,11 +3617,11 @@ void PPUTranslator::LWZ(ppu_opcode_t op) m_may_be_mmio &= (op.ra != 1u && op.ra != 13u); // Stack register and TLS address register are unlikely to be used in MMIO address calculation m_may_be_mmio &= op.simm16 == 0 || spu_thread::test_is_problem_state_register_offset(op.uimm16, true, false); // Either exact MMIO address or MMIO base with completing s16 address offset - if (m_may_be_mmio && !op.simm16) + if (m_may_be_mmio) { struct instructions_data { - be_t insts[2]; + be_t insts[3]; }; // Quick invalidation: expect exact MMIO address, so if the register is being reused with different offset than it's likely not MMIO @@ -3637,6 +3637,12 @@ void PPUTranslator::LWZ(ppu_opcode_t op) continue; } + if (op.simm16 && spu_thread::test_is_problem_state_register_offset(test_op.uimm16, true, false)) + { + // Found register reuse with different MMIO offset + continue; + } + switch (g_ppu_itype.decode(inst)) { case ppu_itype::LWZ: @@ -3714,7 +3720,7 @@ void PPUTranslator::STW(ppu_opcode_t op) m_may_be_mmio &= (op.ra != 1u && op.ra != 13u); // Stack register and TLS address register are unlikely to be used in MMIO address calculation m_may_be_mmio &= op.simm16 == 0 || spu_thread::test_is_problem_state_register_offset(op.uimm16, false, true); // Either exact MMIO address or MMIO base with completing s16 address offset - if (m_may_be_mmio && !op.simm16) + if (m_may_be_mmio) { struct instructions_data { @@ -3734,6 +3740,12 @@ void PPUTranslator::STW(ppu_opcode_t op) continue; } + if (op.simm16 && spu_thread::test_is_problem_state_register_offset(test_op.uimm16, false, true)) + { + // Found register reuse with different MMIO offset + continue; + } + switch (g_ppu_itype.decode(inst)) { case ppu_itype::LWZ: From d0b3891002508a354cfa48c7d456cc1198cfd763 Mon Sep 17 00:00:00 2001 From: Eladash Date: Fri, 4 Aug 2023 20:14:52 +0300 Subject: [PATCH 041/184] PPU LLVM Cache Fixup --- rpcs3/Emu/Cell/PPUThread.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index a4fccecd75..50138efc06 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -3199,7 +3199,7 @@ extern fs::file make_file_view(fs::file&& _file, u64 offset) extern void ppu_finalize(const ppu_module& info) { - if (info.name.empty()) + if (!info.cache.empty()) { // Don't remove main module from memory return; @@ -3226,7 +3226,7 @@ extern void ppu_finalize(const ppu_module& info) fmt::append(cache_path, "ppu-%s-%s/", fmt::base57(info.sha1), info.path.substr(info.path.find_last_of('/') + 1)); #ifdef LLVM_AVAILABLE - g_fxo->get().remove(cache_path + info.name + "_" + std::to_string(info.segs[0].addr)); + g_fxo->get().remove(cache_path + info.name + "_" + std::to_string(std::bit_cast(info.segs[0].ptr))); #endif } @@ -3572,6 +3572,11 @@ extern void ppu_precompile(std::vector& dir_queue, std::vector data) - jit_module& jit_mod = g_fxo->get().get(cache_path + info.name + "_" + std::to_string(info.segs[0].addr)); + jit_module& jit_mod = g_fxo->get().get(cache_path + info.name + "_" + std::to_string(std::bit_cast(info.segs[0].ptr))); // Compiler instance (deferred initialization) std::shared_ptr& jit = jit_mod.pjit; From 4eaa03e9ba4776e459b2ee69a6a71c3bbe7a9550 Mon Sep 17 00:00:00 2001 From: Eladash Date: Sat, 5 Aug 2023 21:33:00 +0300 Subject: [PATCH 042/184] PPU: A few more minor bugfixes --- rpcs3/Emu/Cell/PPUModule.cpp | 16 +++++++++------- rpcs3/Emu/Cell/PPUThread.cpp | 3 --- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index 8ac6832c9d..51c60c31d7 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -1658,6 +1658,11 @@ std::shared_ptr ppu_load_prx(const ppu_prx_object& elf, bool virtual_lo void ppu_unload_prx(const lv2_prx& prx) { + if (prx.segs[0].ptr != vm::base(prx.segs[0].addr)) + { + return; + } + std::unique_lock lock(g_fxo->get().mutex, std::defer_lock); // Clean linkage info @@ -1708,10 +1713,7 @@ void ppu_unload_prx(const lv2_prx& prx) { if (!seg.size) continue; - if (seg.ptr == vm::base(seg.addr)) - { - vm::dealloc(seg.addr, vm::main); - } + vm::dealloc(seg.addr, vm::main); const std::string hash_seg = fmt::format("%s-%u", hash, &seg - prx.segs.data()); @@ -2224,6 +2226,9 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str void init_fxo_for_exec(utils::serial* ar, bool full); init_fxo_for_exec(ar, false); + + liblv2_begin = 0; + liblv2_end = 0; } else { @@ -2231,9 +2236,6 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str Emu.ConfigurePPUCache(); } - liblv2_begin = 0; - liblv2_end = 0; - if (!load_libs.empty()) { for (const auto& name : load_libs) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 50138efc06..67d6ea5f8e 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -3452,7 +3452,6 @@ extern void ppu_precompile(std::vector& dir_queue, std::vector(idm::last_id()); - ppu_unload_prx(*prx); } } From 6547fa9cc315329e127d639d53f23e59405f364d Mon Sep 17 00:00:00 2001 From: Eladash Date: Sat, 5 Aug 2023 21:40:11 +0300 Subject: [PATCH 043/184] PPU LLVM: Fixup VSH Precompilation --- rpcs3/Emu/Cell/PPUThread.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 67d6ea5f8e..60f679be65 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -3645,7 +3645,7 @@ extern void ppu_initialize() const std::string firmware_sprx_path = vfs::get("/dev_flash/sys/external/"); // If empty we have no indication for firmware cache state, check everything - bool compile_fw = true; + bool compile_fw = !Emu.IsVsh(); idm::select([&](u32, lv2_prx& _module) { @@ -3691,7 +3691,7 @@ extern void ppu_initialize() const std::string mount_point = vfs::get("/dev_flash/"); - bool dev_flash_located = !Emu.GetCat().ends_with('P') && Emu.IsPathInsideDir(Emu.GetBoot(), mount_point); + bool dev_flash_located = !Emu.GetCat().ends_with('P') && Emu.IsPathInsideDir(Emu.GetBoot(), mount_point) && g_cfg.core.ppu_llvm_precompilation; if (compile_fw || dev_flash_located) { From 849af08ee94fe830114c2b2a6b3c457d25e6904b Mon Sep 17 00:00:00 2001 From: Eladash Date: Thu, 27 Jul 2023 20:43:14 +0300 Subject: [PATCH 044/184] System.cpp: Fixup spu.log/tty.log dumping --- rpcs3/Emu/System.cpp | 123 ++++++++++++++++++++++--------------------- 1 file changed, 63 insertions(+), 60 deletions(-) diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index eea2619c99..4c909403d8 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -2941,11 +2941,72 @@ void Emulator::Kill(bool allow_autoexit, bool savestate) ar.set_reading_state(); } - // Final termination from main thread (move the last ownership of join thread in order to destroy it) - CallFromMainThread([join_thread = std::move(join_thread), allow_autoexit, this]() mutable + // Log additional debug information - do not do it on the main thread due to the concern of halting UI events + + if (g_tty && sys_log.notice) + { + // Write merged TTY output after emulation has been safely stopped + + if (usz attempted_read_size = utils::sub_saturate(g_tty.pos(), m_tty_file_init_pos)) + { + if (fs::file tty_read_fd{fs::get_cache_dir() + "TTY.log"}) + { + // Enfore an arbitrary limit for now to avoid OOM in case the guest code has bombarded TTY + // 3MB, this should be enough + constexpr usz c_max_tty_spill_size = 0x30'0000; + + std::string tty_buffer(std::min(attempted_read_size, c_max_tty_spill_size), '\0'); + tty_buffer.resize(tty_read_fd.read_at(m_tty_file_init_pos, tty_buffer.data(), tty_buffer.size())); + tty_read_fd.close(); + + if (!tty_buffer.empty()) + { + // Mark start and end very clearly with RPCS3 put in it + sys_log.notice("\nAccumulated RPCS3 TTY:\n\n\n%s\n\n\nEnd RPCS3 TTY Section.\n", tty_buffer); + } + } + } + } + + if (g_cfg.core.spu_debug && sys_log.notice) { const std::string cache_path = rpcs3::cache::get_ppu_cache(); + if (fs::file spu_log{cache_path + "/spu.log"}) + { + // 96MB limit, this may be a lot but this only has an effect when enabling the debug option + constexpr usz c_max_spu_log_spill_size = 0x600'0000; + const usz total_size = spu_log.size(); + + std::string log_buffer(std::min(spu_log.size(), c_max_spu_log_spill_size), '\0'); + log_buffer.resize(spu_log.read(log_buffer.data(), log_buffer.size())); + spu_log.close(); + + if (!log_buffer.empty()) + { + usz to_remove = 0; + usz part_ctr = 1; + + for (std::string_view not_logged = log_buffer; !not_logged.empty(); part_ctr++, not_logged.remove_prefix(to_remove)) + { + std::string_view to_log = not_logged; + to_log = to_log.substr(0, 0x2'0000); + to_log = to_log.substr(0, utils::add_saturate(to_log.rfind("\n========== SPU BLOCK"sv), 1)); + to_remove = to_log.size(); + + // Cannot log it all at once due to technical reasons, split it to 8MB at maximum of whole functions + // Assume the block prefix exists because it is created by RPCS3 (or log it in an ugly manner if it does not exist) + sys_log.notice("Logging spu.log part %u:\n\n%s\n", part_ctr, to_log); + } + + sys_log.notice("End spu.log (%u bytes)", total_size); + } + } + } + + // Final termination from main thread (move the last ownership of join thread in order to destroy it) + CallFromMainThread([join_thread = std::move(join_thread), allow_autoexit, this]() mutable + { cpu_thread::cleanup(); initialize_timebased_time(0, true); @@ -3006,64 +3067,6 @@ void Emulator::Kill(bool allow_autoexit, bool savestate) m_state = system_state::stopped; GetCallbacks().on_stop(); - if (g_tty && sys_log.notice) - { - // Write merged TTY output after emulation has been safely stopped - - if (usz attempted_read_size = utils::sub_saturate(g_tty.pos(), m_tty_file_init_pos)) - { - if (fs::file tty_read_fd{fs::get_cache_dir() + "TTY.log"}) - { - // Enfore an arbitrary limit for now to avoid OOM in case the guest code has bombarded TTY - // 16MB, this should be enough - constexpr usz c_max_tty_spill_size = 0x10'0000; - - std::string tty_buffer(std::min(attempted_read_size, c_max_tty_spill_size), '\0'); - tty_buffer.resize(tty_read_fd.read_at(m_tty_file_init_pos, tty_buffer.data(), tty_buffer.size())); - tty_read_fd.close(); - - if (!tty_buffer.empty()) - { - // Mark start and end very clearly with RPCS3 put in it - sys_log.notice("\nAccumulated RPCS3 TTY:\n\n\n%s\n\n\nEnd RPCS3 TTY Section.\n", tty_buffer); - } - } - } - } - - if (g_cfg.core.spu_debug && sys_log.notice) - { - if (fs::file spu_log{cache_path + "/spu.log"}) - { - // 96MB limit, this may be a lot but this only has an effect when enabling the debug option - constexpr usz c_max_tty_spill_size = 0x60'0000; - - std::string log_buffer(std::min(spu_log.size(), c_max_tty_spill_size), '\0'); - log_buffer.resize(spu_log.read(log_buffer.data(), log_buffer.size())); - spu_log.close(); - - if (!log_buffer.empty()) - { - usz to_remove = 0; - usz part_ctr = 1; - - for (std::string_view not_logged = log_buffer; !not_logged.empty(); part_ctr++, not_logged.remove_prefix(to_remove)) - { - std::string_view to_log = not_logged; - to_log = to_log.substr(0, 0x8'0000); - to_log = to_log.substr(0, utils::add_saturate(to_log.rfind("\n========== SPU BLOCK"sv), 1)); - to_remove = to_log.size(); - - // Cannot log it all at once due to technical reasons, split it to 8MB at maximum of whole functions - // Assume the block prefix exists because it is created by RPCS3 (or log it in an ugly manner if it does not exist) - sys_log.notice("Logging spu.log part %u:\n\n%s\n", part_ctr, to_log); - } - - sys_log.notice("End spu.log"); - } - } - } - // Always Enable display sleep, not only if it was prevented. enable_display_sleep(); From 971c12b93729f10771a149e9addee19b5612ab55 Mon Sep 17 00:00:00 2001 From: Eladash Date: Sun, 6 Aug 2023 07:38:44 +0300 Subject: [PATCH 045/184] Fixup log message in sys_memory_allocate --- rpcs3/Emu/Cell/lv2/sys_memory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/Emu/Cell/lv2/sys_memory.cpp b/rpcs3/Emu/Cell/lv2/sys_memory.cpp index 09a935afac..067902d60c 100644 --- a/rpcs3/Emu/Cell/lv2/sys_memory.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_memory.cpp @@ -131,7 +131,7 @@ error_code sys_memory_allocate(cpu_thread& cpu, u32 size, u64 flags, vm::ptr Date: Sun, 6 Aug 2023 07:57:16 +0300 Subject: [PATCH 046/184] vm.cpp: Fixup race in range_lock_internal --- rpcs3/Emu/Memory/vm.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/rpcs3/Emu/Memory/vm.cpp b/rpcs3/Emu/Memory/vm.cpp index 36e9c2e636..28305052a1 100644 --- a/rpcs3/Emu/Memory/vm.cpp +++ b/rpcs3/Emu/Memory/vm.cpp @@ -174,12 +174,11 @@ namespace vm range_lock->store(to_store); } - for (u64 i = 0, to_clear = umax;; i++) + for (u64 i = 0;; i++) { const u64 is_share = g_shmem[begin >> 16].load(); - to_clear &= get_range_lock_bits(true); - const u64 busy = for_all_range_locks(to_clear, [&](u64 addr_exec, u32 size_exec) + const u64 busy = for_all_range_locks(get_range_lock_bits(true), [&](u64 addr_exec, u32 size_exec) { u64 addr = begin; From 343ba8733b17deec84fc189a1f348aeeb275d7d1 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sun, 23 Jul 2023 09:09:24 +0200 Subject: [PATCH 047/184] Merge xfloat options --- rpcs3/Emu/Cell/SPURecompiler.cpp | 64 +++++++++++++++---------------- rpcs3/Emu/system_config.h | 4 +- rpcs3/Emu/system_config_types.cpp | 17 ++++++++ rpcs3/Emu/system_config_types.h | 8 ++++ rpcs3/rpcs3qt/emu_settings.cpp | 8 ++++ rpcs3/rpcs3qt/emu_settings_type.h | 6 +-- rpcs3/rpcs3qt/settings_dialog.cpp | 25 +----------- 7 files changed, 70 insertions(+), 62 deletions(-) diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index be20bcb8ef..8019a7f24e 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -5316,7 +5316,7 @@ public: if (src > 0x40000) { // Use the xfloat hint to create 256-bit (4x double) PHI - llvm::Type* type = g_cfg.core.spu_accurate_xfloat && bb.reg_maybe_xf[i] ? get_type() : get_reg_type(i); + llvm::Type* type = g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate && bb.reg_maybe_xf[i] ? get_type() : get_reg_type(i); const auto _phi = m_ir->CreatePHI(type, ::size32(bb.preds), fmt::format("phi0x%05x_r%u", baddr, i)); m_block->phi[i] = _phi; @@ -8876,7 +8876,7 @@ public: void FREST(spu_opcode_t op) { // TODO - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { const auto a = get_vr(op.ra); const auto mask_ov = sext(bitcast(fabs(a)) > splat(0x7e7fffff)); @@ -8885,7 +8885,7 @@ public: return; } - if (g_cfg.core.spu_approx_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate) { register_intrinsic("spu_frest", [&](llvm::CallInst* ci) { @@ -8918,13 +8918,13 @@ public: void FRSQEST(spu_opcode_t op) { // TODO - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { set_vr(op.rt, fsplat(1.0) / fsqrt(fabs(get_vr(op.ra)))); return; } - if (g_cfg.core.spu_approx_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate) { register_intrinsic("spu_frsqest", [&](llvm::CallInst* ci) { @@ -8956,7 +8956,7 @@ public: void FCGT(spu_opcode_t op) { - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { set_vr(op.rt, sext(fcmp_ord(get_vr(op.ra) > get_vr(op.rb)))); return; @@ -9003,7 +9003,7 @@ public: return eval(sext(bitcast(a) > bitcast(b))); } - if (g_cfg.core.spu_approx_xfloat || g_cfg.core.spu_relaxed_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate || g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::relaxed) { const auto ai = eval(bitcast(a)); const auto bi = eval(bitcast(b)); @@ -9034,7 +9034,7 @@ public: void FCMGT(spu_opcode_t op) { - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { set_vr(op.rt, sext(fcmp_ord(fabs(get_vr(op.ra)) > fabs(get_vr(op.rb))))); return; @@ -9080,7 +9080,7 @@ public: return eval(sext(mai > mbi)); } - if (g_cfg.core.spu_approx_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate) { return eval(sext(fcmp_uno(ma > mb) & (mai > mbi))); } @@ -9101,7 +9101,7 @@ public: void FA(spu_opcode_t op) { - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { set_vr(op.rt, get_vr(op.ra) + get_vr(op.rb)); return; @@ -9126,7 +9126,7 @@ public: void FS(spu_opcode_t op) { - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { set_vr(op.rt, get_vr(op.ra) - get_vr(op.rb)); return; @@ -9137,7 +9137,7 @@ public: const auto a = value(ci->getOperand(0)); const auto b = value(ci->getOperand(1)); - if (g_cfg.core.spu_approx_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate) { const auto bc = clamp_smax(b); // for #4478 return eval(a - bc); @@ -9159,7 +9159,7 @@ public: void FM(spu_opcode_t op) { - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { set_vr(op.rt, get_vr(op.ra) * get_vr(op.rb)); return; @@ -9170,7 +9170,7 @@ public: const auto a = value(ci->getOperand(0)); const auto b = value(ci->getOperand(1)); - if (g_cfg.core.spu_approx_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate) { if (a.value == b.value) { @@ -9206,7 +9206,7 @@ public: void FESD(spu_opcode_t op) { - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { const auto r = zshuffle(get_vr(op.ra), 1, 3); const auto d = bitcast(r); @@ -9236,7 +9236,7 @@ public: void FRDS(spu_opcode_t op) { - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { const auto r = get_vr(op.ra); const auto d = bitcast(r); @@ -9267,7 +9267,7 @@ public: void FCEQ(spu_opcode_t op) { - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { set_vr(op.rt, sext(fcmp_ord(get_vr(op.ra) == get_vr(op.rb)))); return; @@ -9320,7 +9320,7 @@ public: return eval(sext(bitcast(a) == bitcast(b))); } - if (g_cfg.core.spu_approx_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate) { return eval(sext(fcmp_ord(a == b)) | sext(bitcast(a) == bitcast(b))); } @@ -9341,7 +9341,7 @@ public: void FCMEQ(spu_opcode_t op) { - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { set_vr(op.rt, sext(fcmp_ord(fabs(get_vr(op.ra)) == fabs(get_vr(op.rb))))); return; @@ -9397,7 +9397,7 @@ public: return eval(sext(bitcast(fa) == bitcast(fb))); } - if (g_cfg.core.spu_approx_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate) { return eval(sext(fcmp_ord(fa == fb)) | sext(bitcast(fa) == bitcast(fb))); } @@ -9490,7 +9490,7 @@ public: void FNMS(spu_opcode_t op) { // See FMA. - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { const auto [a, b, c] = get_vrs(op.ra, op.rb, op.rc); set_vr(op.rt4, fmuladd(-a, b, c)); @@ -9503,7 +9503,7 @@ public: const auto b = value(ci->getOperand(1)); const auto c = value(ci->getOperand(2)); - if (g_cfg.core.spu_approx_xfloat || g_cfg.core.spu_relaxed_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate || g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::relaxed) { return fma32x4(eval(-clamp_smax(a)), clamp_smax(b), c); } @@ -9525,7 +9525,7 @@ public: void FMA(spu_opcode_t op) { // Hardware FMA produces the same result as multiple + add on the limited double range (xfloat). - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { const auto [a, b, c] = get_vrs(op.ra, op.rb, op.rc); set_vr(op.rt4, fmuladd(a, b, c)); @@ -9538,7 +9538,7 @@ public: const auto b = value(ci->getOperand(1)); const auto c = value(ci->getOperand(2)); - if (g_cfg.core.spu_approx_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate) { const auto ma = sext(fcmp_uno(a != fsplat(0.))); const auto mb = sext(fcmp_uno(b != fsplat(0.))); @@ -9599,7 +9599,7 @@ public: void FMS(spu_opcode_t op) { // See FMA. - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { const auto [a, b, c] = get_vrs(op.ra, op.rb, op.rc); set_vr(op.rt4, fmuladd(a, b, -c)); @@ -9612,7 +9612,7 @@ public: const auto b = value(ci->getOperand(1)); const auto c = value(ci->getOperand(2)); - if (g_cfg.core.spu_approx_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate) { return fma32x4(clamp_smax(a), clamp_smax(b), eval(-c)); } @@ -9646,7 +9646,7 @@ public: void FI(spu_opcode_t op) { // TODO - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { set_vr(op.rt, get_vr(op.rb)); // const auto [a, b] = get_vrs(op.ra, op.rb); @@ -9674,7 +9674,7 @@ public: return bitcast((b & 0xff800000u) | (bitcast(fpcast(bnew)) & ~0xff800000u)); // Inject old sign and exponent }); - if (g_cfg.core.spu_approx_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate) { register_intrinsic("spu_re", [&](llvm::CallInst* ci) { @@ -9733,7 +9733,7 @@ public: void CFLTS(spu_opcode_t op) { - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { value_t a = get_vr(op.ra); value_t s; @@ -9807,7 +9807,7 @@ public: void CFLTU(spu_opcode_t op) { - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { value_t a = get_vr(op.ra); value_t s; @@ -9890,7 +9890,7 @@ public: void CSFLT(spu_opcode_t op) { - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { value_t a = get_vr(op.ra); value_t r; @@ -9930,7 +9930,7 @@ public: void CUFLT(spu_opcode_t op) { - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { value_t a = get_vr(op.ra); value_t r; diff --git a/rpcs3/Emu/system_config.h b/rpcs3/Emu/system_config.h index a49a7b9c9c..bdce5d0db2 100644 --- a/rpcs3/Emu/system_config.h +++ b/rpcs3/Emu/system_config.h @@ -66,9 +66,7 @@ struct cfg_root : cfg::node cfg::uint<0, 10000> mfc_transfers_timeout{ this, "MFC Commands Timeout", 0, true }; cfg::_bool mfc_shuffling_in_steps{ this, "MFC Commands Shuffling In Steps", false, true }; cfg::_enum enable_TSX{ this, "Enable TSX", enable_tsx_by_default() ? tsx_usage::enabled : tsx_usage::disabled }; // Enable TSX. Forcing this on Haswell/Broadwell CPUs should be used carefully - cfg::_bool spu_accurate_xfloat{ this, "Accurate xfloat", false }; - cfg::_bool spu_approx_xfloat{ this, "Approximate xfloat", true }; - cfg::_bool spu_relaxed_xfloat{ this, "Relaxed xfloat", true }; // Approximate accuracy for only the "FCGT", "FNMS", "FREST" AND "FRSQEST" instructions + cfg::_enum spu_xfloat_accuracy{ this, "XFloat Accuracy", xfloat_accuracy::approximate, false }; cfg::_int<-1, 14> ppu_128_reservations_loop_max_length{ this, "Accurate PPU 128-byte Reservation Op Max Length", 0, true }; // -1: Always accurate, 0: Never accurate, 1-14: max accurate loop length cfg::_int<-64, 64> stub_ppu_traps{ this, "Stub PPU Traps", 0, true }; // Hack, skip PPU traps for rare cases where the trap is continueable (specify relative instructions to skip) cfg::_bool full_width_avx512{ this, "Full Width AVX-512", true }; diff --git a/rpcs3/Emu/system_config_types.cpp b/rpcs3/Emu/system_config_types.cpp index 7324101a89..e785ef8814 100644 --- a/rpcs3/Emu/system_config_types.cpp +++ b/rpcs3/Emu/system_config_types.cpp @@ -664,3 +664,20 @@ void fmt_class_string::format(std::string& out, u64 arg) return unknown; }); } + +template <> +void fmt_class_string::format(std::string& out, u64 arg) +{ + format_enum(out, arg, [](xfloat_accuracy value) + { + switch (value) + { + case xfloat_accuracy::accurate: return "Accurate"; + case xfloat_accuracy::approximate: return "Approximate"; + case xfloat_accuracy::relaxed: return "Relaxed"; + case xfloat_accuracy::inaccurate: return "Inaccurate"; + } + + return unknown; + }); +} diff --git a/rpcs3/Emu/system_config_types.h b/rpcs3/Emu/system_config_types.h index cf1214a967..90862eee69 100644 --- a/rpcs3/Emu/system_config_types.h +++ b/rpcs3/Emu/system_config_types.h @@ -320,3 +320,11 @@ enum class stereo_render_mode_options side_by_side, over_under }; + +enum class xfloat_accuracy +{ + accurate, + approximate, + relaxed, // Approximate accuracy for only the "FCGT", "FNMS", "FREST" AND "FRSQEST" instructions + inaccurate +}; diff --git a/rpcs3/rpcs3qt/emu_settings.cpp b/rpcs3/rpcs3qt/emu_settings.cpp index 37938a9738..03735f8662 100644 --- a/rpcs3/rpcs3qt/emu_settings.cpp +++ b/rpcs3/rpcs3qt/emu_settings.cpp @@ -1283,6 +1283,14 @@ QString emu_settings::GetLocalizedSetting(const QString& original, emu_settings_ case midi_device_type::keyboard: return tr("Keyboard", "Midi Device Type"); } break; + case emu_settings_type::XFloatAccuracy: + switch (static_cast(index)) + { + case xfloat_accuracy::accurate: return tr("Accurate XFloat"); + case xfloat_accuracy::approximate: return tr("Approximate XFloat"); + case xfloat_accuracy::relaxed: return tr("Relaxed XFloat"); + case xfloat_accuracy::inaccurate: return tr("Inaccurate XFloat"); + } default: break; } diff --git a/rpcs3/rpcs3qt/emu_settings_type.h b/rpcs3/rpcs3qt/emu_settings_type.h index f4600d5b87..62d67e43fd 100644 --- a/rpcs3/rpcs3qt/emu_settings_type.h +++ b/rpcs3/rpcs3qt/emu_settings_type.h @@ -26,8 +26,7 @@ enum class emu_settings_type AccurateClineStores, AccurateRSXAccess, FIFOAccuracy, - AccurateXFloat, - ApproximateXFloat, + XFloatAccuracy, AccuratePPU128Loop, MFCCommandsShuffling, NumPPUThreads, @@ -212,8 +211,7 @@ inline static const QMap settings_location = { emu_settings_type::AccurateClineStores, { "Core", "Accurate Cache Line Stores"}}, { emu_settings_type::AccurateRSXAccess, { "Core", "Accurate RSX reservation access"}}, { emu_settings_type::FIFOAccuracy, { "Core", "RSX FIFO Accuracy"}}, - { emu_settings_type::AccurateXFloat, { "Core", "Accurate xfloat"}}, - { emu_settings_type::ApproximateXFloat, { "Core", "Approximate xfloat"}}, + { emu_settings_type::XFloatAccuracy, { "Core", "XFloat Accuracy"}}, { emu_settings_type::MFCCommandsShuffling, { "Core", "MFC Commands Shuffling Limit"}}, { emu_settings_type::SetDAZandFTZ, { "Core", "Set DAZ and FTZ"}}, { emu_settings_type::SPUBlockSize, { "Core", "SPU Block Size"}}, diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index 4952831e99..f153696617 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -265,30 +265,9 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std SubscribeTooltip(ui->spuLoopDetection, tooltips.settings.spu_loop_detection); // Comboboxes + m_emu_settings->EnhanceComboBox(ui->xfloatAccuracy, emu_settings_type::XFloatAccuracy); SubscribeTooltip(ui->gb_xfloat_accuracy, tooltips.settings.xfloat); - ui->xfloatAccuracy->addItem(tr("Accurate XFloat")); - ui->xfloatAccuracy->addItem(tr("Approximate XFloat")); - ui->xfloatAccuracy->addItem(tr("Relaxed XFloat")); - - connect(ui->xfloatAccuracy, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int index) - { - if (index < 0) return; - - m_emu_settings->SetSetting(emu_settings_type::AccurateXFloat, index == 0 ? "true" : "false"); - m_emu_settings->SetSetting(emu_settings_type::ApproximateXFloat, index == 1 ? "true" : "false"); - }); - - connect(m_emu_settings.get(), &emu_settings::RestoreDefaultsSignal, this, [this]() - { - ui->xfloatAccuracy->setCurrentIndex(1); - }); - - if (m_emu_settings->GetSetting(emu_settings_type::AccurateXFloat) == "true") - ui->xfloatAccuracy->setCurrentIndex(0); - else if (m_emu_settings->GetSetting(emu_settings_type::ApproximateXFloat) == "true") - ui->xfloatAccuracy->setCurrentIndex(1); - else - ui->xfloatAccuracy->setCurrentIndex(2); + remove_item(ui->xfloatAccuracy, static_cast(xfloat_accuracy::inaccurate), static_cast(g_cfg.core.spu_xfloat_accuracy.def)); m_emu_settings->EnhanceComboBox(ui->spuBlockSize, emu_settings_type::SPUBlockSize); SubscribeTooltip(ui->gb_spuBlockSize, tooltips.settings.spu_block_size); From ee869a49f4a69d2ce6bb7e417fa4c468ece028ee Mon Sep 17 00:00:00 2001 From: Eladash Date: Sun, 6 Aug 2023 09:43:13 +0300 Subject: [PATCH 048/184] PPU Precompilation Fixup --- rpcs3/Emu/Cell/PPUAnalyser.cpp | 5 +++++ rpcs3/Emu/Cell/PPUModule.cpp | 2 +- rpcs3/Emu/Cell/PPUThread.cpp | 7 +++++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUAnalyser.cpp b/rpcs3/Emu/Cell/PPUAnalyser.cpp index ebda9c2580..2a0186213c 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.cpp +++ b/rpcs3/Emu/Cell/PPUAnalyser.cpp @@ -532,6 +532,11 @@ namespace ppu_patterns bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::basic_string& applied, std::function check_aborted) { + if (segs.empty()) + { + return false; + } + // Assume first segment is executable const u32 start = segs[0].addr; diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index 51c60c31d7..060b767c7e 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -1658,7 +1658,7 @@ std::shared_ptr ppu_load_prx(const ppu_prx_object& elf, bool virtual_lo void ppu_unload_prx(const lv2_prx& prx) { - if (prx.segs[0].ptr != vm::base(prx.segs[0].addr)) + if (prx.segs.empty() || prx.segs[0].ptr != vm::base(prx.segs[0].addr)) { return; } diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 60f679be65..b339116c97 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -3199,7 +3199,7 @@ extern fs::file make_file_view(fs::file&& _file, u64 offset) extern void ppu_finalize(const ppu_module& info) { - if (!info.cache.empty()) + if (info.name.empty()) { // Don't remove main module from memory return; @@ -3573,7 +3573,7 @@ extern void ppu_precompile(std::vector& dir_queue, std::vector& dir_queue, std::vector& dir_queue, std::vectorget() = std::move(main_module); + Emu.ConfigurePPUCache(); }); exec_worker(); From 2a0278fbb1828662c40f25de7920a2cdd6bc8670 Mon Sep 17 00:00:00 2001 From: Eladash Date: Sun, 6 Aug 2023 12:24:32 +0300 Subject: [PATCH 049/184] Fixup SPU/PPU Cache Abortion --- rpcs3/Emu/Cell/PPUThread.cpp | 5 +++++ rpcs3/Emu/Cell/SPURecompiler.cpp | 5 +++++ rpcs3/Emu/system_progress.cpp | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index b339116c97..a052453071 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -1823,6 +1823,11 @@ void ppu_thread::cpu_task() // We don't want to open a cell dialog while a native progress dialog is still open. while (u32 v = g_progr_ptotal) { + if (Emu.IsStopped()) + { + return; + } + g_progr_ptotal.wait(v); } diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index 8019a7f24e..49ff59a856 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -649,6 +649,11 @@ void spu_cache::initialize() // Initialize progress dialog (wait for previous progress done) while (u32 v = g_progr_ptotal) { + if (Emu.IsStopped()) + { + break; + } + g_progr_ptotal.wait(v); } diff --git a/rpcs3/Emu/system_progress.cpp b/rpcs3/Emu/system_progress.cpp index 6ce68a668e..3869262349 100644 --- a/rpcs3/Emu/system_progress.cpp +++ b/rpcs3/Emu/system_progress.cpp @@ -226,6 +226,11 @@ void progress_dialog_server::operator()() native_dlg->set_text("Stopping. Please wait..."); native_dlg->refresh(); } + + if (g_progr_ptotal.exchange(0)) + { + g_progr_ptotal.notify_all(); + } } progress_dialog_server::~progress_dialog_server() From c96f3a877f37f3a9bd0903c0f5c6190aaad35f1c Mon Sep 17 00:00:00 2001 From: Eladash Date: Sun, 6 Aug 2023 14:02:36 +0300 Subject: [PATCH 050/184] PPU Analyzer: Add more function constraints --- rpcs3/Emu/Cell/PPUAnalyser.cpp | 40 ++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUAnalyser.cpp b/rpcs3/Emu/Cell/PPUAnalyser.cpp index 2a0186213c..f878e22cb2 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.cpp +++ b/rpcs3/Emu/Cell/PPUAnalyser.cpp @@ -556,6 +556,32 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b // Known references (within segs, addr and value alignment = 4) std::set addr_heap{entry}; + auto verify_func = [&](u32 addr) + { + if (!entry) + { + // Fixed addresses + return true; + } + + // Check if the storage address exists within relocations + + for (auto& rel : this->relocs) + { + if ((rel.addr & -8) == (addr & -8)) + { + if (rel.type != 38 && rel.type != 44 && (rel.addr & -4) != (addr & -4)) + { + continue; + } + + return true; + } + } + + return false; + }; + // Register new function auto add_func = [&](u32 addr, u32 toc, u32 caller) -> ppu_function& { @@ -617,7 +643,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b for (; _ptr <= seg_end;) { - if (ptr[1] == toc && ptr[0] >= start && ptr[0] < end && ptr[0] % 4 == 0) + if (ptr[1] == toc && ptr[0] >= start && ptr[0] < end && ptr[0] % 4 == 0 && verify_func(_ptr.addr())) { // New function ppu_log.trace("OPD*: [0x%x] 0x%x (TOC=0x%x)", _ptr, ptr[0], ptr[1]); @@ -700,13 +726,13 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b //const u32 _toc_end = _toc + 0x8000; // TODO: improve TOC constraints - if (_toc % 4 || !get_ptr(_toc) || _toc >= 0x40000000 || (_toc >= start && _toc < end)) + if (_toc % 4 || !get_ptr(_toc) || _toc >= 0x40000000 || (_toc >= start && _toc < end)) { sec_end.set(0); break; } - if (addr % 4 || addr < start || addr >= end || addr == _toc) + if (addr % 4 || addr < start || addr >= end || addr == _toc || !verify_func(_ptr.addr())) { sec_end.set(0); break; @@ -956,7 +982,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b // Simple trampoline const u32 target = (ptr[0] << 16) + ppu_opcode_t{ptr[1]}.simm16; - if (target >= start && target < end) + if (target >= start && target < end && verify_func(_ptr.addr())) { auto& new_func = add_func(target, func.toc, func.addr); @@ -1027,7 +1053,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b const u32 target = (ptr[3] << 16) + s16(ptr[4]); const u32 toc_add = (ptr[1] << 16) + s16(ptr[2]); - if (target >= start && target < end) + if (target >= start && target < end && verify_func((_ptr + 3).addr())) { auto& new_func = add_func(target, 0, func.addr); @@ -1074,7 +1100,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b const u32 toc_add = (ptr[1] << 16) + s16(ptr[2]); const u32 target = (ptr[3] & 0x2 ? 0 : (_ptr + 3).addr()) + ppu_opcode_t{ptr[3]}.bt24; - if (target >= start && target < end) + if (target >= start && target < end && verify_func((_ptr + 3).addr())) { auto& new_func = add_func(target, 0, func.addr); @@ -1437,7 +1463,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b { const u32 target = (op.aa ? 0 : iaddr) + (type == ppu_itype::B ? +op.bt24 : +op.bt14); - if (target >= start && target < end) + if (target >= start && target < end && verify_func((_ptr - 1).addr())) { if (target < func.addr || target >= func.addr + func.size) { From 506ec0f94757b557b635aaeeaf0ebf7221ef1725 Mon Sep 17 00:00:00 2001 From: Eladash Date: Sun, 6 Aug 2023 19:47:23 +0300 Subject: [PATCH 051/184] PPU Loader: Fix imports/exports in virtual mode --- rpcs3/Emu/Cell/PPUAnalyser.h | 6 +-- rpcs3/Emu/Cell/PPUModule.cpp | 99 +++++++++++++++++++++--------------- 2 files changed, 60 insertions(+), 45 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUAnalyser.h b/rpcs3/Emu/Cell/PPUAnalyser.h index cde7fb7949..c2965267cb 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.h +++ b/rpcs3/Emu/Cell/PPUAnalyser.h @@ -168,19 +168,19 @@ struct ppu_module } template requires requires (const U& obj) { +obj.size() * 0; } - to_be_t& get_ref(U&& addr, + to_be_t& get_ref(U&& addr, u32 index = 0, u32 line = __builtin_LINE(), u32 col = __builtin_COLUMN(), const char* file = __builtin_FILE(), const char* func = __builtin_FUNCTION()) const { constexpr usz size_element = std::is_void_v ? 0 : sizeof(std::conditional_t, char, T>); - if (auto ptr = get_ptr(addr.addr(), u32{size_element})) + if (auto ptr = get_ptr((addr + index).addr(), u32{size_element})) { return *ptr; } - fmt::throw_exception("get_ref(): Failure! (addr=0x%x)%s", addr.addr(), src_loc{line, col, file, func}); + fmt::throw_exception("get_ref(): Failure! (addr=0x%x)%s", (addr + index).addr(), src_loc{line, col, file, func}); return *std::add_pointer_t>{}; } }; diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index 060b767c7e..a8deaa53ff 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -552,7 +552,7 @@ extern const std::unordered_map& get_exported_function_na } // Resolve relocations for variable/function linkage. -static void ppu_patch_refs(std::vector* out_relocs, u32 fref, u32 faddr) +static void ppu_patch_refs(const ppu_module& _module, std::vector* out_relocs, u32 fref, u32 faddr) { struct ref_t { @@ -561,7 +561,7 @@ static void ppu_patch_refs(std::vector* out_relocs, u32 fref, u32 fad be_t addend; // Note: Treating it as addend seems to be correct for now, but still unknown if theres more in this variable }; - for (auto ref = vm::ptr::make(fref); ref->type; ref++) + for (const ref_t* ref = &_module.get_ref(fref); ref->type; fref += sizeof(ref_t), ref = &_module.get_ref(fref)) { if (ref->addend) ppu_loader.warning("**** REF(%u): Addend value(0x%x, 0x%x)", ref->type, ref->addr, ref->addend); @@ -584,28 +584,28 @@ static void ppu_patch_refs(std::vector* out_relocs, u32 fref, u32 fad { case 1: { - const u32 value = vm::_ref(ref->addr) = rdata; + const u32 value = _module.get_ref(ref->addr) = rdata; ppu_loader.trace("**** REF(1): 0x%x <- 0x%x", ref->addr, value); break; } case 4: { - const u16 value = vm::_ref(ref->addr) = static_cast(rdata); + const u16 value = _module.get_ref(ref->addr) = static_cast(rdata); ppu_loader.trace("**** REF(4): 0x%x <- 0x%04x (0x%llx)", ref->addr, value, faddr); break; } case 6: { - const u16 value = vm::_ref(ref->addr) = static_cast(rdata >> 16) + (rdata & 0x8000 ? 1 : 0); + const u16 value = _module.get_ref(ref->addr) = static_cast(rdata >> 16) + (rdata & 0x8000 ? 1 : 0); ppu_loader.trace("**** REF(6): 0x%x <- 0x%04x (0x%llx)", ref->addr, value, faddr); break; } case 57: { - const u16 value = vm::_ref, 0, 14>>(ref->addr) = static_cast(rdata) >> 2; + const u16 value = _module.get_ref, 0, 14>>(ref->addr) = static_cast(rdata) >> 2; ppu_loader.trace("**** REF(57): 0x%x <- 0x%04x (0x%llx)", ref->addr, value, faddr); break; } @@ -680,7 +680,7 @@ extern bool ppu_register_library_lock(std::string_view libname, bool lock_lib) } // Load and register exports; return special exports found (nameless module) -static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 exports_end, bool for_observing_callbacks = false, std::basic_string* loaded_flags = nullptr) +static auto ppu_load_exports(const ppu_module& _module, ppu_linkage_info* link, u32 exports_start, u32 exports_end, bool for_observing_callbacks = false, std::basic_string* loaded_flags = nullptr) { std::unordered_map result; @@ -694,7 +694,7 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo for (u32 addr = exports_start; addr < exports_end; unload_index++, addr += lib.size ? lib.size : sizeof(ppu_prx_module_info)) { - std::memcpy(&lib, vm::base(addr), sizeof(lib)); + std::memcpy(&lib, &_module.get_ref(addr), sizeof(lib)); const bool is_library = !!(lib.attributes & PRX_EXPORT_LIBRARY_FLAG); const bool is_management = !is_library && !!(lib.attributes & PRX_EXPORT_PRX_MANAGEMENT_FUNCTIONS_FLAG); @@ -709,12 +709,12 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo // Set special exports for (u32 i = 0, end = lib.num_func + lib.num_var; i < end; i++) { - const u32 nid = lib.nids[i]; - const u32 addr = lib.addrs[i]; + const u32 nid = _module.get_ref(lib.nids, i); + const u32 addr = _module.get_ref(lib.addrs, i); if (i < lib.num_func) { - ppu_loader.notice("** Special: [%s] at 0x%x [0x%x, 0x%x]", ppu_get_function_name({}, nid), addr, vm::_ref(addr), vm::_ref(addr + 4)); + ppu_loader.notice("** Special: [%s] at 0x%x [0x%x, 0x%x]", ppu_get_function_name({}, nid), addr, _module.get_ref(addr), _module.get_ref(addr + 4)); } else { @@ -738,7 +738,7 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo continue; } - const std::string module_name(lib.name.get_ptr()); + const std::string module_name(&_module.get_ref(lib.name)); if (unload_exports) { @@ -782,9 +782,9 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo // Get functions for (u32 i = 0, end = lib.num_func; i < end; i++) { - const u32 fnid = fnids[i]; - const u32 faddr = faddrs[i]; - ppu_loader.notice("**** %s export: [%s] (0x%08x) at 0x%x [at:0x%x]", module_name, ppu_get_function_name(module_name, fnid), fnid, faddr, vm::read32(faddr)); + const u32 fnid = _module.get_ref(fnids, i); + const u32 faddr = _module.get_ref(faddrs, i); + ppu_loader.notice("**** %s export: [%s] (0x%08x) at 0x%x [at:0x%x]", module_name, ppu_get_function_name(module_name, fnid), fnid, faddr, _module.get_ref(faddr)); // Function linkage info auto& flink = mlink.functions[fnid]; @@ -811,7 +811,10 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo // Set exported function flink.export_addr = target - 4; - ppu_form_branch_to_code(vm::read32(faddr), target); + if (auto ptr = _module.get_ptr(faddr); vm::try_get_addr(ptr).first) + { + ppu_form_branch_to_code(*ptr, target); + } } else { @@ -821,13 +824,13 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo // Fix imports for (const u32 addr : flink.imports) { - vm::write32(addr, faddr); + _module.get_ref(addr) = faddr; //ppu_loader.warning("Exported function '%s' in module '%s'", ppu_get_function_name(module_name, fnid), module_name); } for (const u32 fref : flink.frefss) { - ppu_patch_refs(nullptr, fref, faddr); + ppu_patch_refs(_module, nullptr, fref, faddr); } } } @@ -839,8 +842,8 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo // Get variables for (u32 i = 0, end = lib.num_var; i < end; i++) { - const u32 vnid = vnids[i]; - const u32 vaddr = vaddrs[i]; + const u32 vnid = _module.get_ref(vnids, i); + const u32 vaddr = _module.get_ref(vaddrs, i); ppu_loader.notice("**** %s export: &[%s] at 0x%x", module_name, ppu_get_variable_name(module_name, vnid), vaddr); // Variable linkage info @@ -863,7 +866,7 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo // Fix imports for (const auto vref : vlink.imports) { - ppu_patch_refs(nullptr, vref, vaddr); + ppu_patch_refs(_module, nullptr, vref, vaddr); //ppu_loader.warning("Exported variable '%s' in module '%s'", ppu_get_variable_name(module_name, vnid), module_name); } } @@ -873,17 +876,17 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo return result; } -static auto ppu_load_imports(std::vector& relocs, ppu_linkage_info* link, u32 imports_start, u32 imports_end) +static auto ppu_load_imports(const ppu_module& _module, std::vector& relocs, ppu_linkage_info* link, u32 imports_start, u32 imports_end) { std::unordered_map result; - reader_lock lock(link->mutex); + std::lock_guard lock(link->mutex); for (u32 addr = imports_start; addr < imports_end;) { - const auto& lib = vm::_ref(addr); + const auto& lib = _module.get_ref(addr); - const std::string module_name(lib.name.get_ptr()); + const std::string module_name(&_module.get_ref(lib.name)); ppu_loader.notice("** Imported module '%s' (ver=0x%x, attr=0x%x, 0x%x, 0x%x) [0x%x]", module_name, lib.version, lib.attributes, lib.unk4, lib.unk5, addr); @@ -903,8 +906,8 @@ static auto ppu_load_imports(std::vector& relocs, ppu_linkage_info* l for (u32 i = 0, end = lib.num_func; i < end; i++) { - const u32 fnid = fnids[i]; - const u32 fstub = faddrs[i]; + const u32 fnid = _module.get_ref(fnids, i); + const u32 fstub = _module.get_ref(faddrs, i); const u32 faddr = (faddrs + i).addr(); ppu_loader.notice("**** %s import: [%s] (0x%08x) -> 0x%x", module_name, ppu_get_function_name(module_name, fnid), fnid, fstub); @@ -920,14 +923,14 @@ static auto ppu_load_imports(std::vector& relocs, ppu_linkage_info* l const u32 link_addr = flink.export_addr ? flink.export_addr : g_fxo->get().addr; // Write import table - vm::write32(faddr, link_addr); + _module.get_ref(faddr) = link_addr; // Patch refs if necessary (0x2000 seems to be correct flag indicating the presence of additional info) - if (const u32 frefs = (lib.attributes & 0x2000) ? +fnids[i + lib.num_func] : 0) + if (const u32 frefs = (lib.attributes & 0x2000) ? +_module.get_ref(fnids, i + lib.num_func) : 0) { result.emplace(frefs, &flink); flink.frefss.emplace(frefs); - ppu_patch_refs(&relocs, frefs, link_addr); + ppu_patch_refs(_module, &relocs, frefs, link_addr); } //ppu_loader.warning("Imported function '%s' in module '%s' (0x%x)", ppu_get_function_name(module_name, fnid), module_name, faddr); @@ -938,8 +941,8 @@ static auto ppu_load_imports(std::vector& relocs, ppu_linkage_info* l for (u32 i = 0, end = lib.num_var; i < end; i++) { - const u32 vnid = vnids[i]; - const u32 vref = vstubs[i]; + const u32 vnid = _module.get_ref(vnids, i); + const u32 vref = _module.get_ref(vstubs, i); ppu_loader.notice("**** %s import: &[%s] (ref=*0x%x)", module_name, ppu_get_variable_name(module_name, vnid), vref); // Variable linkage info @@ -951,7 +954,7 @@ static auto ppu_load_imports(std::vector& relocs, ppu_linkage_info* l mlink.imported = true; // Link if available - ppu_patch_refs(&relocs, vref, vlink.export_addr); + ppu_patch_refs(_module, &relocs, vref, vlink.export_addr); //ppu_loader.warning("Imported variable '%s' in module '%s' (0x%x)", ppu_get_variable_name(module_name, vnid), module_name, vlink.first); } @@ -968,14 +971,18 @@ void ppu_manual_load_imports_exports(u32 imports_start, u32 imports_size, u32 ex auto& _main = g_fxo->get(); auto& link = g_fxo->get(); - ppu_load_exports(&link, exports_start, exports_start + exports_size, false, &loaded_flags); + ppu_module vm_all_fake_module{}; + vm_all_fake_module.segs.emplace_back(ppu_segment{0x10000, -0x10000u, 1 /*LOAD*/, 0, -0x1000u, vm::base(0x10000)}); + vm_all_fake_module.addr_to_seg_index.emplace(0x10000, 0); + + ppu_load_exports(vm_all_fake_module, &link, exports_start, exports_start + exports_size, false, &loaded_flags); if (!imports_size) { return; } - ppu_load_imports(_main.relocs, &link, imports_start, imports_start + imports_size); + ppu_load_imports(vm_all_fake_module, _main.relocs, &link, imports_start, imports_start + imports_size); } // For savestates @@ -1569,10 +1576,14 @@ std::shared_ptr ppu_load_prx(const ppu_prx_object& elf, bool virtual_lo ppu_loader.warning("Library %s (rtoc=0x%x):", lib_name, lib_info->toc); - if (!virtual_load) + ppu_linkage_info dummy{}; + + prx->specials = ppu_load_exports(*prx, virtual_load ? &dummy : &link, prx->exports_start, prx->exports_end, true); + prx->imports = ppu_load_imports(*prx, prx->relocs, virtual_load ? &dummy : &link, lib_info->imports_start, lib_info->imports_end); + + if (virtual_load) { - prx->specials = ppu_load_exports(&link, prx->exports_start, prx->exports_end, true); - prx->imports = ppu_load_imports(prx->relocs, &link, lib_info->imports_start, lib_info->imports_end); + prx->imports.clear(); } std::stable_sort(prx->relocs.begin(), prx->relocs.end()); @@ -2105,10 +2116,12 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str return false; } + ppu_linkage_info dummy{}; + if (!virtual_load) { - ppu_load_exports(&link, proc_prx_param.libent_start, proc_prx_param.libent_end); - ppu_load_imports(_main.relocs, &link, proc_prx_param.libstub_start, proc_prx_param.libstub_end); + ppu_load_exports(_main, virtual_load ? &dummy : &link, proc_prx_param.libent_start, proc_prx_param.libent_end); + ppu_load_imports(_main, _main.relocs, virtual_load ? &dummy : &link, proc_prx_param.libstub_start, proc_prx_param.libstub_end); } std::stable_sort(_main.relocs.begin(), _main.relocs.end()); @@ -2667,10 +2680,12 @@ std::pair, CellError> ppu_load_overlay(const ppu_ex fmt::throw_exception("Bad magic! (0x%x)", proc_prx_param.magic); } + ppu_linkage_info dummy{}; + if (!virtual_load) { - ppu_load_exports(&link, proc_prx_param.libent_start, proc_prx_param.libent_end); - ppu_load_imports(ovlm->relocs, &link, proc_prx_param.libstub_start, proc_prx_param.libstub_end); + ppu_load_exports(*ovlm, virtual_load ? &dummy : &link, proc_prx_param.libent_start, proc_prx_param.libent_end); + ppu_load_imports(*ovlm, ovlm->relocs, virtual_load ? &dummy : &link, proc_prx_param.libstub_start, proc_prx_param.libstub_end); } } break; From 39910885d90b37484b6e8851fd04236fd5128c2a Mon Sep 17 00:00:00 2001 From: Eladash Date: Sun, 6 Aug 2023 21:04:48 +0300 Subject: [PATCH 052/184] PPU: fix leak when precompiling SELF files --- rpcs3/Emu/Cell/PPUThread.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index a052453071..b3f2da3006 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -3231,7 +3231,7 @@ extern void ppu_finalize(const ppu_module& info) fmt::append(cache_path, "ppu-%s-%s/", fmt::base57(info.sha1), info.path.substr(info.path.find_last_of('/') + 1)); #ifdef LLVM_AVAILABLE - g_fxo->get().remove(cache_path + info.name + "_" + std::to_string(std::bit_cast(info.segs[0].ptr))); + g_fxo->get().remove(cache_path + "_" + std::to_string(std::bit_cast(info.segs[0].ptr))); #endif } @@ -3588,9 +3588,8 @@ extern void ppu_precompile(std::vector& dir_queue, std::vector data) - jit_module& jit_mod = g_fxo->get().get(cache_path + info.name + "_" + std::to_string(std::bit_cast(info.segs[0].ptr))); + jit_module& jit_mod = g_fxo->get().get(cache_path + "_" + std::to_string(std::bit_cast(info.segs[0].ptr))); // Compiler instance (deferred initialization) std::shared_ptr& jit = jit_mod.pjit; From 91b68f3b457a1425b47db7eac100d0be238e8d05 Mon Sep 17 00:00:00 2001 From: Eladash Date: Mon, 7 Aug 2023 06:02:51 +0300 Subject: [PATCH 053/184] PPU Analyzer: Fixup verify_func --- rpcs3/Emu/Cell/PPUAnalyser.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUAnalyser.cpp b/rpcs3/Emu/Cell/PPUAnalyser.cpp index f878e22cb2..f680b6907e 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.cpp +++ b/rpcs3/Emu/Cell/PPUAnalyser.cpp @@ -558,7 +558,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b auto verify_func = [&](u32 addr) { - if (!entry) + if (entry) { // Fixed addresses return true; @@ -643,7 +643,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b for (; _ptr <= seg_end;) { - if (ptr[1] == toc && ptr[0] >= start && ptr[0] < end && ptr[0] % 4 == 0 && verify_func(_ptr.addr())) + if (ptr[1] == toc && FN(x >= start && x < end && x % 4 == 0)(ptr[0]) && verify_func(_ptr.addr())) { // New function ppu_log.trace("OPD*: [0x%x] 0x%x (TOC=0x%x)", _ptr, ptr[0], ptr[1]); @@ -726,7 +726,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b //const u32 _toc_end = _toc + 0x8000; // TODO: improve TOC constraints - if (_toc % 4 || !get_ptr(_toc) || _toc >= 0x40000000 || (_toc >= start && _toc < end)) + if (_toc % 4 || !get_ptr(_toc) || _toc >= 0x40000000 || (_toc >= start && _toc < end)) { sec_end.set(0); break; @@ -954,7 +954,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b continue; } - if (target >= start && target < end) + if (target >= start && target < end && (~ptr[0] & 0x2 || verify_func(_ptr.addr()))) { auto& new_func = add_func(target, func.toc, func.addr); @@ -1100,7 +1100,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b const u32 toc_add = (ptr[1] << 16) + s16(ptr[2]); const u32 target = (ptr[3] & 0x2 ? 0 : (_ptr + 3).addr()) + ppu_opcode_t{ptr[3]}.bt24; - if (target >= start && target < end && verify_func((_ptr + 3).addr())) + if (target >= start && target < end && (~ptr[3] & 0x2 || verify_func((_ptr + 3).addr()))) { auto& new_func = add_func(target, 0, func.addr); @@ -1463,7 +1463,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b { const u32 target = (op.aa ? 0 : iaddr) + (type == ppu_itype::B ? +op.bt24 : +op.bt14); - if (target >= start && target < end && verify_func((_ptr - 1).addr())) + if (target >= start && target < end && (op.aa && verify_func(iaddr))) { if (target < func.addr || target >= func.addr + func.size) { From 63f045ef2e1ff14600ada00be1276d36d28e3a3e Mon Sep 17 00:00:00 2001 From: Eladash Date: Fri, 21 Oct 2022 13:16:30 +0300 Subject: [PATCH 054/184] Add some error checking to sys_fs_chmod --- rpcs3/Emu/Cell/lv2/sys_fs.cpp | 56 +++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/rpcs3/Emu/Cell/lv2/sys_fs.cpp b/rpcs3/Emu/Cell/lv2/sys_fs.cpp index bfcf0c09ec..dabbb03268 100644 --- a/rpcs3/Emu/Cell/lv2/sys_fs.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_fs.cpp @@ -2773,6 +2773,62 @@ error_code sys_fs_chmod(ppu_thread&, vm::cptr path, s32 mode) { sys_fs.todo("sys_fs_chmod(path=%s, mode=%#o)", path, mode); + const auto [path_error, vpath] = translate_to_sv(path); + + if (path_error) + { + return {path_error, vpath}; + } + + const std::string local_path = vfs::get(vpath); + + const auto mp = lv2_fs_object::get_mp(vpath); + + if (local_path.empty()) + { + return {CELL_ENOTMOUNTED, path}; + } + + if (mp->flags & lv2_mp_flag::read_only) + { + return {CELL_EROFS, path}; + } + + std::unique_lock lock(mp->mutex); + + fs::stat_t info{}; + + if (!fs::get_stat(local_path, info)) + { + switch (auto error = fs::g_tls_error) + { + case fs::error::noent: + { + // Try to locate split files + + for (u32 i = 66601; i <= 66699; i++) + { + if (!fs::get_stat(fmt::format("%s.%u", local_path, i), info) && !info.is_directory) + { + break; + } + } + + if (fs::get_stat(local_path + ".66600", info) && !info.is_directory) + { + break; + } + + return {CELL_ENOENT, path}; + } + default: + { + sys_fs.error("sys_fs_chmod(): unknown error %s", error); + return {CELL_EIO, path}; + } + } + } + return CELL_OK; } From 0989f6225655a7ca20e964a706d76f7814b24b55 Mon Sep 17 00:00:00 2001 From: Eladash Date: Fri, 21 Oct 2022 12:37:41 +0300 Subject: [PATCH 055/184] cellGame: Add some LV2 sleep Those functions are supposed to take a really long time, and in this time the caller PPU waits for VSH. --- rpcs3/Emu/Cell/Modules/cellGame.cpp | 127 +++++++++++++++++++++++++--- 1 file changed, 114 insertions(+), 13 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellGame.cpp b/rpcs3/Emu/Cell/Modules/cellGame.cpp index 8b5ffb8765..61f9dbc95a 100644 --- a/rpcs3/Emu/Cell/Modules/cellGame.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGame.cpp @@ -349,6 +349,29 @@ void disc_change_manager::insert_disc(u32 disc_type, std::string title_id) }); } +void lv2_sleep(u64 timeout, ppu_thread* ppu = nullptr) +{ + if (!ppu) + { + ppu = ensure(cpu_thread::get_current()); + } + + if (!timeout) + { + return; + } + + const bool had_wait = ppu->state.test_and_set(cpu_flag::wait); + + lv2_obj::sleep(*ppu); + lv2_obj::wait_timeout(timeout); + ppu->check_state(); + + if (had_wait) + { + ppu->state += cpu_flag::wait; + } +} error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr dirName, u32 errDialog, vm::ptr funcStat, u32 container) { @@ -434,6 +457,8 @@ error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr dirName // TODO ? + lv2_sleep(5000, &ppu); + funcStat(ppu, result, get, set); std::string error_msg; @@ -445,6 +470,8 @@ error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr dirName // Game confirmed that it wants to create directory const auto setParam = set->setParam; + lv2_sleep(2000, &ppu); + if (new_data) { if (!setParam) @@ -536,6 +563,10 @@ error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr dirName return CELL_GAMEDATA_ERROR_INTERNAL; } } + else + { + lv2_sleep(2000, &ppu); + } return CELL_HDDGAME_ERROR_CBRESULT; } @@ -548,14 +579,24 @@ error_code cellHddGameCheck2(ppu_thread& ppu, u32 version, vm::cptr dirNam return cellHddGameCheck(ppu, version, dirName, errDialog, funcStat, container); } -error_code cellHddGameGetSizeKB(vm::ptr size) +error_code cellHddGameGetSizeKB(ppu_thread& ppu, vm::ptr size) { + ppu.state += cpu_flag::wait; + cellGame.warning("cellHddGameGetSizeKB(size=*0x%x)", size); + lv2_obj::sleep(ppu); + + const u64 start_sleep = ppu.start_time; + const std::string local_dir = vfs::get(Emu.GetDir()); const auto dirsz = fs::get_dir_size(local_dir, 1024); + // This function is very slow by nature + // TODO: Check if after first use the result is being cached so the sleep can be reduced in this case + lv2_sleep(utils::sub_saturate(dirsz == umax ? 2000 : 200000, get_guest_system_time() - start_sleep), &ppu); + if (dirsz == umax) { const auto error = fs::g_tls_error; @@ -568,7 +609,8 @@ error_code cellHddGameGetSizeKB(vm::ptr size) return CELL_HDDGAME_ERROR_FAILURE; } - *size = ::narrow(dirsz / 1024); + ppu.check_state(); + *size = ::narrow(dirsz / 1024); return CELL_OK; } @@ -591,8 +633,10 @@ error_code cellHddGameExitBroken() return open_exit_dialog(get_localized_string(localized_string_id::CELL_HDD_GAME_EXIT_BROKEN), true); } -error_code cellGameDataGetSizeKB(vm::ptr size) +error_code cellGameDataGetSizeKB(ppu_thread& ppu, vm::ptr size) { + ppu.state += cpu_flag::wait; + cellGame.warning("cellGameDataGetSizeKB(size=*0x%x)", size); if (!size) @@ -600,10 +644,18 @@ error_code cellGameDataGetSizeKB(vm::ptr size) return CELL_GAMEDATA_ERROR_PARAM; } + lv2_obj::sleep(ppu); + + const u64 start_sleep = ppu.start_time; + const std::string local_dir = vfs::get(Emu.GetDir()); const auto dirsz = fs::get_dir_size(local_dir, 1024); + // This function is very slow by nature + // TODO: Check if after first use the result is being cached so the sleep can be reduced in this case + lv2_sleep(utils::sub_saturate(dirsz == umax ? 2000 : 200000, get_guest_system_time() - start_sleep), &ppu); + if (dirsz == umax) { const auto error = fs::g_tls_error; @@ -616,7 +668,8 @@ error_code cellGameDataGetSizeKB(vm::ptr size) return CELL_GAMEDATA_ERROR_FAILURE; } - *size = ::narrow(dirsz / 1024); + ppu.check_state(); + *size = ::narrow(dirsz / 1024); return CELL_OK; } @@ -650,6 +703,8 @@ error_code cellGameBootCheck(vm::ptr type, vm::ptr attributes, vm::ptr auto& perm = g_fxo->get(); + lv2_sleep(5000); + const auto init = perm.init.init(); if (!init) @@ -662,11 +717,13 @@ error_code cellGameBootCheck(vm::ptr type, vm::ptr attributes, vm::ptr const std::string& cat = Emu.GetFakeCat(); + u32 _type{}; + if (cat == "DG") { perm.mode = content_permission::check_mode::disc_game; - *type = CELL_GAME_GAMETYPE_DISC; + _type = CELL_GAME_GAMETYPE_DISC; *attributes = 0; // TODO // TODO: dirName might be a read only string when BootCheck is called on a disc game. (e.g. Ben 10 Ultimate Alien: Cosmic Destruction) @@ -676,7 +733,7 @@ error_code cellGameBootCheck(vm::ptr type, vm::ptr attributes, vm::ptr { perm.mode = content_permission::check_mode::patch; - *type = CELL_GAME_GAMETYPE_DISC; + _type = CELL_GAME_GAMETYPE_DISC; *attributes = CELL_GAME_ATTRIBUTE_PATCH; // TODO sfo = psf::load_object(vfs::get(Emu.GetDir() + "PARAM.SFO")); @@ -685,13 +742,15 @@ error_code cellGameBootCheck(vm::ptr type, vm::ptr attributes, vm::ptr { perm.mode = content_permission::check_mode::hdd_game; - *type = CELL_GAME_GAMETYPE_HDD; + _type = CELL_GAME_GAMETYPE_HDD; *attributes = 0; // TODO sfo = psf::load_object(vfs::get(Emu.GetDir() + "PARAM.SFO")); dir = Emu.GetTitleID(); } + *type = _type; + if (size) { // TODO: Use the free space of the computer's HDD where RPCS3 is being run. @@ -702,7 +761,7 @@ error_code cellGameBootCheck(vm::ptr type, vm::ptr attributes, vm::ptr size->sysSizeKB = 4; } - if (*type == u32{CELL_GAME_GAMETYPE_HDD} && dirName) + if (_type == u32{CELL_GAME_GAMETYPE_HDD} && dirName) { strcpy_trunc(*dirName, Emu.GetTitleID()); } @@ -718,6 +777,8 @@ error_code cellGamePatchCheck(vm::ptr size, vm::ptr r { cellGame.warning("cellGamePatchCheck(size=*0x%x, reserved=*0x%x)", size, reserved); + lv2_sleep(5000); + if (Emu.GetCat() != "GD") { return CELL_GAME_ERROR_NOTPATCH; @@ -770,6 +831,8 @@ error_code cellGameDataCheck(u32 type, vm::cptr dirName, vm::ptrget(); @@ -826,7 +889,7 @@ error_code cellGameDataCheck(u32 type, vm::cptr dirName, vm::ptr contentInfoPath, vm::ptr usrdirPath) +error_code cellGameContentPermit(ppu_thread& ppu, vm::ptr contentInfoPath, vm::ptr usrdirPath) { cellGame.warning("cellGameContentPermit(contentInfoPath=*0x%x, usrdirPath=*0x%x)", contentInfoPath, usrdirPath); @@ -854,6 +917,10 @@ error_code cellGameContentPermit(vm::ptr contentInfoPa return CELL_OK; } + lv2_obj::sleep(ppu); + + const u64 start_sleep = ppu.start_time; + if (!perm.temp.empty()) { // Create PARAM.SFO @@ -882,6 +949,9 @@ error_code cellGameContentPermit(vm::ptr contentInfoPa ensure(temp.commit()); } + // This function is very slow by nature + lv2_sleep(utils::sub_saturate(!perm.temp.empty() || perm.can_create ? 200000 : 2000, get_guest_system_time() - start_sleep), &ppu); + // Cleanup perm.reset(); @@ -892,7 +962,7 @@ error_code cellGameContentPermit(vm::ptr contentInfoPa error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr dirName, u32 errDialog, vm::ptr funcStat, u32 container) { - cellGame.error("cellGameDataCheckCreate2(version=0x%x, dirName=%s, errDialog=0x%x, funcStat=*0x%x, container=%d)", version, dirName, errDialog, funcStat, container); + cellGame.success("cellGameDataCheckCreate2(version=0x%x, dirName=%s, errDialog=0x%x, funcStat=*0x%x, container=%d)", version, dirName, errDialog, funcStat, container); //older sdk. it might not care about game type. @@ -954,6 +1024,8 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr strcpy_trunc(cbGet->getParam.titleLang[i], psf::get_string(sfo, fmt::format("TITLE_%02d", i))); } + lv2_sleep(5000, &ppu); + funcStat(ppu, cbResult, cbGet, cbSet); std::string error_msg; @@ -970,6 +1042,8 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr // Game confirmed that it wants to create directory const auto setParam = cbSet->setParam; + lv2_sleep(2000, &ppu); + if (new_data) { if (!setParam) @@ -1065,6 +1139,10 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr return CELL_GAMEDATA_ERROR_INTERNAL; } } + else + { + lv2_sleep(2000, &ppu); + } return CELL_GAMEDATA_ERROR_CBRESULT; } @@ -1079,7 +1157,7 @@ error_code cellGameDataCheckCreate(ppu_thread& ppu, u32 version, vm::cptr error_code cellGameCreateGameData(vm::ptr init, vm::ptr tmp_contentInfoPath, vm::ptr tmp_usrdirPath) { - cellGame.error("cellGameCreateGameData(init=*0x%x, tmp_contentInfoPath=*0x%x, tmp_usrdirPath=*0x%x)", init, tmp_contentInfoPath, tmp_usrdirPath); + cellGame.success("cellGameCreateGameData(init=*0x%x, tmp_contentInfoPath=*0x%x, tmp_usrdirPath=*0x%x)", init, tmp_contentInfoPath, tmp_usrdirPath); if (!init) { @@ -1090,6 +1168,8 @@ error_code cellGameCreateGameData(vm::ptr init, vm::ptr init, vm::ptr value) return CELL_GAME_ERROR_PARAM; } + lv2_sleep(2000); + auto& perm = g_fxo->get(); const auto init = perm.init.access(); @@ -1359,6 +1444,8 @@ error_code cellGameGetParamString(s32 id, vm::ptr buf, u32 bufsize) auto& perm = g_fxo->get(); + lv2_sleep(2000); + const auto init = perm.init.access(); if (!init || perm.mode == content_permission::check_mode::not_set) @@ -1401,6 +1488,8 @@ error_code cellGameSetParamString(s32 id, vm::cptr buf) return CELL_GAME_ERROR_PARAM; } + lv2_sleep(2000); + auto& perm = g_fxo->get(); const auto init = perm.init.access(); @@ -1427,7 +1516,7 @@ error_code cellGameSetParamString(s32 id, vm::cptr buf) return CELL_OK; } -error_code cellGameGetSizeKB(vm::ptr size) +error_code cellGameGetSizeKB(ppu_thread& ppu, vm::ptr size) { cellGame.warning("cellGameGetSizeKB(size=*0x%x)", size); @@ -1438,6 +1527,7 @@ error_code cellGameGetSizeKB(vm::ptr size) // Always reset to 0 at start *size = 0; + ppu.state += cpu_flag::wait; auto& perm = g_fxo->get(); @@ -1448,10 +1538,18 @@ error_code cellGameGetSizeKB(vm::ptr size) return CELL_GAME_ERROR_FAILURE; } + lv2_obj::sleep(ppu); + + const u64 start_sleep = ppu.start_time; + const std::string local_dir = !perm.temp.empty() ? perm.temp : vfs::get("/dev_hdd0/game/" + perm.dir); const auto dirsz = fs::get_dir_size(local_dir, 1024); + // This function is very slow by nature + // TODO: Check if after first use the result is being cached so the sleep can be reduced in this case + lv2_sleep(utils::sub_saturate(dirsz == umax ? 1000 : 200000, get_guest_system_time() - start_sleep), &ppu); + if (dirsz == umax) { const auto error = fs::g_tls_error; @@ -1467,7 +1565,8 @@ error_code cellGameGetSizeKB(vm::ptr size) } } - *size = ::narrow(dirsz / 1024); + ppu.check_state(); + *size = ::narrow(dirsz / 1024); return CELL_OK; } @@ -1708,6 +1807,8 @@ error_code cellDiscGameGetBootDiscInfo(vm::ptr getP // Always sets 0 at first dword write_to_ptr(getParam->titleId, 0); + lv2_sleep(2000); + // This is also called by non-disc games, see NPUB90029 static const std::string dir = "/dev_bdvd/PS3_GAME"s; From d69460014600a44debded76d4d495c967ea439b8 Mon Sep 17 00:00:00 2001 From: Eladash Date: Mon, 7 Aug 2023 15:44:22 +0300 Subject: [PATCH 056/184] PPU Loader: Fixup virtual load for non PRX --- rpcs3/Emu/Cell/PPUModule.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index a8deaa53ff..2d42accef6 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -2118,11 +2118,8 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str ppu_linkage_info dummy{}; - if (!virtual_load) - { - ppu_load_exports(_main, virtual_load ? &dummy : &link, proc_prx_param.libent_start, proc_prx_param.libent_end); - ppu_load_imports(_main, _main.relocs, virtual_load ? &dummy : &link, proc_prx_param.libstub_start, proc_prx_param.libstub_end); - } + ppu_load_exports(_main, virtual_load ? &dummy : &link, proc_prx_param.libent_start, proc_prx_param.libent_end); + ppu_load_imports(_main, _main.relocs, virtual_load ? &dummy : &link, proc_prx_param.libstub_start, proc_prx_param.libstub_end); std::stable_sort(_main.relocs.begin(), _main.relocs.end()); } @@ -2682,11 +2679,8 @@ std::pair, CellError> ppu_load_overlay(const ppu_ex ppu_linkage_info dummy{}; - if (!virtual_load) - { - ppu_load_exports(*ovlm, virtual_load ? &dummy : &link, proc_prx_param.libent_start, proc_prx_param.libent_end); - ppu_load_imports(*ovlm, ovlm->relocs, virtual_load ? &dummy : &link, proc_prx_param.libstub_start, proc_prx_param.libstub_end); - } + ppu_load_exports(*ovlm, virtual_load ? &dummy : &link, proc_prx_param.libent_start, proc_prx_param.libent_end); + ppu_load_imports(*ovlm, ovlm->relocs, virtual_load ? &dummy : &link, proc_prx_param.libstub_start, proc_prx_param.libstub_end); } break; } From 8e75e940ddbe99778ddada110f6fda6ff713a174 Mon Sep 17 00:00:00 2001 From: Eladash Date: Mon, 7 Aug 2023 17:08:19 +0300 Subject: [PATCH 057/184] cellSaveData: Fix check 58, implement checks 77 and 76 --- rpcs3/Emu/Cell/Modules/cellSaveData.cpp | 35 +++++++++++++++++-------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellSaveData.cpp b/rpcs3/Emu/Cell/Modules/cellSaveData.cpp index 648c94b225..6e078fe650 100644 --- a/rpcs3/Emu/Cell/Modules/cellSaveData.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSaveData.cpp @@ -1547,21 +1547,34 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v return {CELL_SAVEDATA_ERROR_PARAM, "57 (attribute=0x%x)", statSet->setParam->attribute}; } - if (g_ps3_process_info.sdk_ver > 0x36FFFF) + if (statSet->setParam->parental_level > 11) { - // In firmware 3.70 or higher parental_level was changed to reserved2 and has to zeroes - if (statSet->setParam->parental_level) - { - // ****** sysutil savedata parameter error : 58 ****** - return {CELL_SAVEDATA_ERROR_PARAM, "58 (sdk_ver=0x%x, parental_level=%d)", g_ps3_process_info.sdk_ver, statSet->setParam->parental_level}; - } + // ****** sysutil savedata parameter error : 58 ****** + return {CELL_SAVEDATA_ERROR_PARAM, "58 (sdk_ver=0x%x, parental_level=%d)", g_ps3_process_info.sdk_ver, statSet->setParam->parental_level}; } - else + + // Note: in firmware 3.70 or higher parental_level was changed to reserved2 + + for (usz index = 0;; index++) { - if (statSet->setParam->parental_level > 11) + // Convert to pointer to avoid UB when accessing out of range + const u8 c = (+statSet->setParam->listParam)[index]; + + if (c == 0 || index >= (g_ps3_process_info.sdk_ver > 0x36FFFF ? std::size(statSet->setParam->listParam) - 1 : std::size(statSet->setParam->listParam))) { - // ****** sysutil savedata parameter error : 58 ****** - return {CELL_SAVEDATA_ERROR_PARAM, "58 (sdk_ver=0x%x, parental_level=%d)", g_ps3_process_info.sdk_ver, statSet->setParam->parental_level}; + if (c) + { + // ****** sysutil savedata parameter error : 76 ****** + return {CELL_SAVEDATA_ERROR_PARAM, "76 (listParam=0x%016x)", std::bit_cast>(statSet->setParam->listParam)}; + } + + break; + } + + if ((c < 'A' || c > 'Z') && (c < '0' || c > '9') && c != '-' && c != '_') + { + // ****** sysutil savedata parameter error : 77 ****** + return {CELL_SAVEDATA_ERROR_PARAM, "77 (listParam=0x%016x)", std::bit_cast>(statSet->setParam->listParam)}; } } From edf4f7eacc4d1c87fc1fade32f1662e49e2748ad Mon Sep 17 00:00:00 2001 From: Eladash Date: Mon, 7 Aug 2023 18:33:47 +0300 Subject: [PATCH 058/184] PPU/Patches: Fix CALLOC patch with interpreter --- rpcs3/Emu/Cell/PPUThread.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index b3f2da3006..9936fc4076 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -3769,10 +3769,18 @@ bool ppu_initialize(const ppu_module& info, bool check_only) { for (auto& block : func.blocks) { + const auto targets = g_fxo->get().get_targets(block.first, block.second); + + if (!targets.empty()) + { + // Replace the block with ppu_far_jump + continue; + } + ppu_register_function_at(block.first, block.second); } - if (g_cfg.core.ppu_debug && func.size && func.toc != umax) + if (g_cfg.core.ppu_debug && func.size && func.toc != umax && !ppu_get_far_jump(func.addr)) { ppu_toc[func.addr] = func.toc; ppu_ref(func.addr) = &ppu_check_toc; From cdc0441405eda0cf91ae959b39f3722c94995d4e Mon Sep 17 00:00:00 2001 From: Eladash Date: Mon, 7 Aug 2023 21:33:36 +0300 Subject: [PATCH 059/184] PPU: Fixup interpreter and analyzer --- rpcs3/Emu/Cell/PPUAnalyser.cpp | 2 +- rpcs3/Emu/Cell/PPUThread.cpp | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUAnalyser.cpp b/rpcs3/Emu/Cell/PPUAnalyser.cpp index f680b6907e..2a774d0fd3 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.cpp +++ b/rpcs3/Emu/Cell/PPUAnalyser.cpp @@ -1463,7 +1463,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b { const u32 target = (op.aa ? 0 : iaddr) + (type == ppu_itype::B ? +op.bt24 : +op.bt14); - if (target >= start && target < end && (op.aa && verify_func(iaddr))) + if (target >= start && target < end && (!op.aa || verify_func(iaddr))) { if (target < func.addr || target >= func.addr + func.size) { diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 9936fc4076..27067bb33b 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -3769,9 +3769,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only) { for (auto& block : func.blocks) { - const auto targets = g_fxo->get().get_targets(block.first, block.second); - - if (!targets.empty()) + if (g_fxo->is_init() && !g_fxo->get().get_targets(block.first, block.second).empty()) { // Replace the block with ppu_far_jump continue; From df24305698acee9a40332317d70546cfa658de55 Mon Sep 17 00:00:00 2001 From: Eladash Date: Tue, 8 Aug 2023 09:48:12 +0300 Subject: [PATCH 060/184] PPU/Debugger: Fix op_branch_targets --- rpcs3/Emu/Cell/PPUThread.cpp | 4 +--- rpcs3/util/fixed_typemap.hpp | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 27067bb33b..eb9f967fce 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -1206,9 +1206,7 @@ std::array op_branch_targets(u32 pc, ppu_opcode_t op) { std::array res{pc + 4, umax}; - g_fxo->need(); - - if (u32 target = g_fxo->get().get_target(pc)) + if (u32 target = g_fxo->is_init() ? g_fxo->get().get_target(pc) : 0) { res[0] = target; return res; diff --git a/rpcs3/util/fixed_typemap.hpp b/rpcs3/util/fixed_typemap.hpp index 896d8ae779..3c2ee8e7f2 100644 --- a/rpcs3/util/fixed_typemap.hpp +++ b/rpcs3/util/fixed_typemap.hpp @@ -347,7 +347,7 @@ namespace stx } // Check if object is not initialized but shall be initialized first (to use in initializing other objects) - template + template requires (std::is_constructible_v || std::is_default_constructible_v) void need() noexcept { if (!m_init[stx::typeindex>()]) From bd7715b18068332dbafac7064312a410457d7314 Mon Sep 17 00:00:00 2001 From: Eladash Date: Tue, 8 Aug 2023 10:01:51 +0300 Subject: [PATCH 061/184] ELF.h: fix warning --- rpcs3/Loader/ELF.h | 1 - 1 file changed, 1 deletion(-) diff --git a/rpcs3/Loader/ELF.h b/rpcs3/Loader/ELF.h index 831e58dea1..abb9979c19 100644 --- a/rpcs3/Loader/ELF.h +++ b/rpcs3/Loader/ELF.h @@ -456,7 +456,6 @@ public: // Rely on previous sh_offset value! if (hdr.p_offset <= shdr.sh_offset && shdr.sh_offset + shdr.sh_size - 1 <= hdr.p_offset + hdr.p_filesz - 1) { - const auto& prog = ::at32(progs, p_index); out.sh_offset = data_base + shdr.sh_offset - hdr.p_offset; result = true; break; From 767979ea44b88df535c1d710051b7a7bb41c6685 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Mon, 7 Aug 2023 23:30:12 +0300 Subject: [PATCH 062/184] rsx: Implement image copy between 2D -> 3C/CUBE with scaling support. --- rpcs3/Emu/RSX/GL/GLTextureCache.cpp | 4 +- rpcs3/Emu/RSX/VK/VKTextureCache.cpp | 93 ++++++++++++++++------------- 2 files changed, 52 insertions(+), 45 deletions(-) diff --git a/rpcs3/Emu/RSX/GL/GLTextureCache.cpp b/rpcs3/Emu/RSX/GL/GLTextureCache.cpp index e5a2888708..45ab652d32 100644 --- a/rpcs3/Emu/RSX/GL/GLTextureCache.cpp +++ b/rpcs3/Emu/RSX/GL/GLTextureCache.cpp @@ -258,14 +258,12 @@ namespace gl } else { - ensure(dst_image->get_target() == gl::texture::target::texture2D); - auto _blitter = gl::g_hw_blitter; const areai src_rect = { src_x, src_y, src_x + src_w, src_y + src_h }; const areai dst_rect = { slice.dst_x, slice.dst_y, slice.dst_x + slice.dst_w, slice.dst_y + slice.dst_h }; gl::texture* _dst; - if (src_image->get_internal_format() == dst_image->get_internal_format() && slice.level == 0) + if (src_image->get_internal_format() == dst_image->get_internal_format() && slice.level == 0 && slice.dst_z == 0) { _dst = dst_image; } diff --git a/rpcs3/Emu/RSX/VK/VKTextureCache.cpp b/rpcs3/Emu/RSX/VK/VKTextureCache.cpp index b0344fc9e6..4d85eb437a 100644 --- a/rpcs3/Emu/RSX/VK/VKTextureCache.cpp +++ b/rpcs3/Emu/RSX/VK/VKTextureCache.cpp @@ -276,7 +276,32 @@ namespace vk for (const auto& section : sections_to_transfer) { if (!section.src) + { continue; + } + + // Generates a region to write data to the final destination + const auto get_output_region = [&](s32 in_x, s32 in_y, u32 w, u32 h, vk::image* data_src) + { + VkImageCopy copy_rgn = { + .srcSubresource = { data_src->aspect(), 0, 0, 1}, + .srcOffset = { in_x, in_y, 0 }, + .dstSubresource = { dst_aspect, section.level, 0, 1 }, + .dstOffset = { section.dst_x, section.dst_y, 0 }, + .extent = { w, h, 1 } + }; + + if (dst->info.imageType == VK_IMAGE_TYPE_3D) + { + copy_rgn.dstOffset.z = section.dst_z; + } + else + { + copy_rgn.dstSubresource.baseArrayLayer = section.dst_z; + } + + return copy_rgn; + }; const bool typeless = section.src->aspect() != dst_aspect || !formats_are_bitcast_compatible(dst, section.src); @@ -333,11 +358,12 @@ namespace vk src_image->change_layout(cmd, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); const areai src_rect = coordi{{ src_x, src_y }, { src_w, src_h }}; - const areai dst_rect = coordi{{ convert_x, src_y }, { convert_w, src_h }}; + const areai dst_rect = coordi{{ 0, 0 }, { convert_w, src_h }}; vk::copy_image_typeless(cmd, section.src, src_image, src_rect, dst_rect, 1); src_image->change_layout(cmd, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - src_x = convert_x; + src_x = 0; + src_y = 0; src_w = convert_w; } @@ -345,51 +371,47 @@ namespace vk // Final aspect mask of the 'final' transfer source const auto new_src_aspect = src_image->aspect(); + const bool require_scaling = src_w != section.dst_w || src_h != section.dst_h; - if (src_w == section.dst_w && src_h == section.dst_h && transform == rsx::surface_transform::identity) [[likely]] + if (!require_scaling && transform == rsx::surface_transform::identity) [[likely]] { - VkImageCopy copy_rgn; - copy_rgn.srcOffset = { src_x, src_y, 0 }; - copy_rgn.dstOffset = { section.dst_x, section.dst_y, 0 }; - copy_rgn.dstSubresource = { dst_aspect, 0, 0, 1 }; - copy_rgn.srcSubresource = { new_src_aspect, 0, 0, 1 }; - copy_rgn.extent = { src_w, src_h, 1 }; - - if (dst->info.imageType == VK_IMAGE_TYPE_3D) - { - copy_rgn.dstOffset.z = section.dst_z; - } - else - { - copy_rgn.dstSubresource.baseArrayLayer = section.dst_z; - copy_rgn.dstSubresource.mipLevel = section.level; - } - + const auto copy_rgn = get_output_region(src_x, src_y, src_w, src_h, src_image); vkCmdCopyImage(cmd, src_image->value, src_image->current_layout, dst->value, dst->current_layout, 1, ©_rgn); } else { - ensure(section.dst_z == 0); - u16 dst_x = section.dst_x, dst_y = section.dst_y; vk::image* _dst; - if (src_image->info.format == dst->info.format && section.level == 0) [[likely]] + // Check for best-case scenario - we write directly to the output in this case + if (src_image->info.format == dst->info.format && + section.level == 0 && + section.dst_z == 0) [[likely]] { _dst = dst; } else { // Either a bitcast is required or a scale+copy to mipmap level - _dst = vk::get_typeless_helper(src_image->format(), src_image->format_class(), dst->width(), dst->height() * 2); + const u32 requested_width = dst->width(); + const u32 requested_height = src_y + src_h + section.dst_h; + _dst = vk::get_typeless_helper(src_image->format(), src_image->format_class(), requested_width, requested_height); _dst->change_layout(cmd, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); } + if (_dst == src_image) + { + // Write-to-self situation. Account for the initial typeless copy. + ensure(dst != _dst); + dst_x = 0; + dst_y = src_y + src_h; + } + if (transform == rsx::surface_transform::identity) { vk::copy_scaled_image(cmd, src_image, _dst, coordi{ { src_x, src_y }, { src_w, src_h } }, - coordi{ { section.dst_x, section.dst_y }, { section.dst_w, section.dst_h } }, + coordi{ { dst_x, dst_y }, { section.dst_w, section.dst_h } }, 1, src_image->format() == _dst->format(), VK_FILTER_NEAREST); } @@ -413,23 +435,16 @@ namespace vk vk::insert_buffer_memory_barrier(cmd, scratch_buf->value, 0, mem_length, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT); - auto tmp = vk::get_typeless_helper(src_image->format(), src_image->format_class(), section.dst_x + section.dst_w, section.dst_y + section.dst_h); + auto tmp = vk::get_typeless_helper(src_image->format(), src_image->format_class(), section.dst_w, section.dst_h); tmp->change_layout(cmd, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + dst_x = dst_y = 0; copy.imageOffset = { 0, 0, 0 }; vkCmdCopyBufferToImage(cmd, scratch_buf->value, tmp->value, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©); - dst_x = 0; - dst_y = 0; - - if (src_w != section.dst_w || src_h != section.dst_h) + if (require_scaling) { // Optionally scale if needed - if (tmp == _dst) [[unlikely]] - { - dst_y = src_h; - } - vk::copy_scaled_image(cmd, tmp, _dst, areai{ 0, 0, src_w, static_cast(src_h) }, coordi{ { dst_x, dst_y }, { section.dst_w, section.dst_h } }, @@ -449,13 +464,7 @@ namespace vk if (_dst != dst) [[unlikely]] { // Casting comes after the scaling! - VkImageCopy copy_rgn; - copy_rgn.srcOffset = { s32(dst_x), s32(dst_y), 0 }; - copy_rgn.dstOffset = { section.dst_x, section.dst_y, 0 }; - copy_rgn.dstSubresource = { dst_aspect, section.level, 0, 1 }; - copy_rgn.srcSubresource = { _dst->aspect(), 0, 0, 1 }; - copy_rgn.extent = { section.dst_w, section.dst_h, 1 }; - + const auto copy_rgn = get_output_region(dst_x, dst_y, section.dst_w, section.dst_h, _dst); _dst->change_layout(cmd, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); vkCmdCopyImage(cmd, _dst->value, _dst->current_layout, dst->value, dst->current_layout, 1, ©_rgn); } From 58e9e54b7fab59cf70e44daeb008aad7abe91c60 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Mon, 7 Aug 2023 23:41:27 +0300 Subject: [PATCH 063/184] rsx: Remove unused code (argb<->bgra modifier) and refactor a bit --- rpcs3/Emu/RSX/Common/texture_cache_helpers.h | 3 +- rpcs3/Emu/RSX/GL/GLTextureCache.cpp | 13 ++-- rpcs3/Emu/RSX/VK/VKTextureCache.cpp | 78 +++----------------- 3 files changed, 16 insertions(+), 78 deletions(-) diff --git a/rpcs3/Emu/RSX/Common/texture_cache_helpers.h b/rpcs3/Emu/RSX/Common/texture_cache_helpers.h index ea2196a876..629f40888a 100644 --- a/rpcs3/Emu/RSX/Common/texture_cache_helpers.h +++ b/rpcs3/Emu/RSX/Common/texture_cache_helpers.h @@ -11,8 +11,7 @@ namespace rsx enum surface_transform : u32 { identity = 0, // Nothing - argb_to_bgra = 1, // Swap ARGB to BGRA (endian swap) - coordinate_transform = 2 // Incoming source coordinates may generated based on the format of the secondary (dest) surface. Recalculate them before use. + coordinate_transform = 1 // Incoming source coordinates may generated based on the format of the secondary (dest) surface. Recalculate them before use. }; template diff --git a/rpcs3/Emu/RSX/GL/GLTextureCache.cpp b/rpcs3/Emu/RSX/GL/GLTextureCache.cpp index 45ab652d32..9ef77cb05c 100644 --- a/rpcs3/Emu/RSX/GL/GLTextureCache.cpp +++ b/rpcs3/Emu/RSX/GL/GLTextureCache.cpp @@ -193,7 +193,9 @@ namespace gl for (const auto &slice : sources) { if (!slice.src) + { continue; + } const bool typeless = !formats_are_bitcast_compatible(slice.src, dst_image); ensure(typeless || dst_aspect == slice.src->aspect()); @@ -262,19 +264,14 @@ namespace gl const areai src_rect = { src_x, src_y, src_x + src_w, src_y + src_h }; const areai dst_rect = { slice.dst_x, slice.dst_y, slice.dst_x + slice.dst_w, slice.dst_y + slice.dst_h }; - gl::texture* _dst; - if (src_image->get_internal_format() == dst_image->get_internal_format() && slice.level == 0 && slice.dst_z == 0) - { - _dst = dst_image; - } - else + gl::texture* _dst = dst_image; + if (src_image->get_internal_format() != dst_image->get_internal_format() || slice.level != 0 || slice.dst_z != 0) [[ unlikely ]] { tmp = std::make_unique(GL_TEXTURE_2D, dst_rect.x2, dst_rect.y2, 1, 1, static_cast(slice.src->get_internal_format())); _dst = tmp.get(); } - _blitter->scale_image(cmd, src_image, _dst, - src_rect, dst_rect, false, {}); + _blitter->scale_image(cmd, src_image, _dst, src_rect, dst_rect, false, {}); if (_dst != dst_image) { diff --git a/rpcs3/Emu/RSX/VK/VKTextureCache.cpp b/rpcs3/Emu/RSX/VK/VKTextureCache.cpp index 4d85eb437a..a22783addf 100644 --- a/rpcs3/Emu/RSX/VK/VKTextureCache.cpp +++ b/rpcs3/Emu/RSX/VK/VKTextureCache.cpp @@ -368,12 +368,9 @@ namespace vk } ensure(src_image->current_layout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL || src_image->current_layout == VK_IMAGE_LAYOUT_GENERAL); + ensure(transform == rsx::surface_transform::identity); - // Final aspect mask of the 'final' transfer source - const auto new_src_aspect = src_image->aspect(); - const bool require_scaling = src_w != section.dst_w || src_h != section.dst_h; - - if (!require_scaling && transform == rsx::surface_transform::identity) [[likely]] + if (src_w == section.dst_w && src_h == section.dst_h) [[likely]] { const auto copy_rgn = get_output_region(src_x, src_y, src_w, src_h, src_image); vkCmdCopyImage(cmd, src_image->value, src_image->current_layout, dst->value, dst->current_layout, 1, ©_rgn); @@ -381,18 +378,11 @@ namespace vk else { u16 dst_x = section.dst_x, dst_y = section.dst_y; - vk::image* _dst; + vk::image* _dst = dst; - // Check for best-case scenario - we write directly to the output in this case - if (src_image->info.format == dst->info.format && - section.level == 0 && - section.dst_z == 0) [[likely]] + if (src_image->info.format != dst->info.format || section.level != 0 || section.dst_z != 0) [[ unlikely ]] { - _dst = dst; - } - else - { - // Either a bitcast is required or a scale+copy to mipmap level + // Either a bitcast is required or a scale+copy to mipmap level / layer const u32 requested_width = dst->width(); const u32 requested_height = src_y + src_h + section.dst_h; _dst = vk::get_typeless_helper(src_image->format(), src_image->format_class(), requested_width, requested_height); @@ -407,59 +397,11 @@ namespace vk dst_y = src_y + src_h; } - if (transform == rsx::surface_transform::identity) - { - vk::copy_scaled_image(cmd, src_image, _dst, - coordi{ { src_x, src_y }, { src_w, src_h } }, - coordi{ { dst_x, dst_y }, { section.dst_w, section.dst_h } }, - 1, src_image->format() == _dst->format(), - VK_FILTER_NEAREST); - } - else if (transform == rsx::surface_transform::argb_to_bgra) - { - VkBufferImageCopy copy{}; - copy.imageExtent = { src_w, src_h, 1 }; - copy.imageOffset = { src_x, src_y, 0 }; - copy.imageSubresource = { src_image->aspect(), 0, 0, 1 }; - - const auto mem_length = src_w * src_h * dst_bpp; - auto scratch_buf = vk::get_scratch_buffer(cmd, mem_length); - vkCmdCopyImageToBuffer(cmd, src_image->value, src_image->current_layout, scratch_buf->value, 1, ©); - - vk::insert_buffer_memory_barrier(cmd, scratch_buf->value, 0, mem_length, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT); - - auto shuffle_kernel = vk::get_compute_task(); - shuffle_kernel->run(cmd, scratch_buf, mem_length); - - vk::insert_buffer_memory_barrier(cmd, scratch_buf->value, 0, mem_length, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT); - - auto tmp = vk::get_typeless_helper(src_image->format(), src_image->format_class(), section.dst_w, section.dst_h); - tmp->change_layout(cmd, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - - dst_x = dst_y = 0; - copy.imageOffset = { 0, 0, 0 }; - vkCmdCopyBufferToImage(cmd, scratch_buf->value, tmp->value, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©); - - if (require_scaling) - { - // Optionally scale if needed - vk::copy_scaled_image(cmd, tmp, _dst, - areai{ 0, 0, src_w, static_cast(src_h) }, - coordi{ { dst_x, dst_y }, { section.dst_w, section.dst_h } }, - 1, tmp->info.format == _dst->info.format, - VK_FILTER_NEAREST); - } - else - { - _dst = tmp; - } - } - else - { - fmt::throw_exception("Unreachable"); - } + vk::copy_scaled_image(cmd, src_image, _dst, + coordi{ { src_x, src_y }, { src_w, src_h } }, + coordi{ { dst_x, dst_y }, { section.dst_w, section.dst_h } }, + 1, src_image->format() == _dst->format(), + VK_FILTER_NEAREST); if (_dst != dst) [[unlikely]] { From a2416bf7f5042568b385104f20ed216accd0da3b Mon Sep 17 00:00:00 2001 From: Eladash Date: Tue, 8 Aug 2023 11:03:39 +0300 Subject: [PATCH 064/184] cellPad: Fix max_connect in GetInfo This value is saved and loaded from cellPadInit as is. --- rpcs3/Emu/Cell/Modules/cellPad.cpp | 26 +++++++++++++------------- rpcs3/Emu/Cell/Modules/cellPad.h | 5 +++++ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellPad.cpp b/rpcs3/Emu/Cell/Modules/cellPad.cpp index 629a7e3c93..075d5dbb4e 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPad.cpp @@ -79,7 +79,7 @@ error_code cellPadInit(u32 max_connect) return CELL_PAD_ERROR_INVALID_PARAMETER; libio_sys_config_init(); - config.max_connect = std::min(max_connect, CELL_PAD_MAX_PORT_NUM); + config.max_connect = max_connect; config.port_setting.fill(CELL_PAD_SETTING_PRESS_OFF | CELL_PAD_SETTING_SENSOR_OFF); return CELL_OK; } @@ -138,7 +138,7 @@ error_code cellPadClearBuf(u32 port_no) const auto& pads = handler->GetPads(); - if (port_no >= config.max_connect) + if (port_no >= config.get_max_connect()) return CELL_PAD_ERROR_NO_DEVICE; const auto& pad = pads[port_no]; @@ -171,7 +171,7 @@ error_code cellPadGetData(u32 port_no, vm::ptr data) const auto& pads = handler->GetPads(); - if (port_no >= config.max_connect) + if (port_no >= config.get_max_connect()) return CELL_PAD_ERROR_NO_DEVICE; const auto& pad = pads[port_no]; @@ -413,7 +413,7 @@ error_code cellPadPeriphGetInfo(vm::ptr info) for (u32 i = 0; i < CELL_PAD_MAX_PORT_NUM; ++i) { - if (i >= config.max_connect) + if (i >= config.get_max_connect()) break; info->port_status[i] = pads[i]->m_port_status; @@ -447,7 +447,7 @@ error_code cellPadPeriphGetData(u32 port_no, vm::ptr data) const auto& pads = handler->GetPads(); - if (port_no >= config.max_connect) + if (port_no >= config.get_max_connect()) return CELL_PAD_ERROR_NO_DEVICE; const auto& pad = pads[port_no]; @@ -482,7 +482,7 @@ error_code cellPadGetRawData(u32 port_no, vm::ptr data) const auto& pads = handler->GetPads(); - if (port_no >= config.max_connect) + if (port_no >= config.get_max_connect()) return CELL_PAD_ERROR_NO_DEVICE; const auto& pad = pads[port_no]; @@ -548,7 +548,7 @@ error_code cellPadSetActDirect(u32 port_no, vm::ptr param) const auto& pads = handler->GetPads(); - if (port_no >= config.max_connect) + if (port_no >= config.get_max_connect()) return CELL_PAD_ERROR_NO_DEVICE; const auto& pad = pads[port_no]; @@ -592,7 +592,7 @@ error_code cellPadGetInfo(vm::ptr info) for (u32 i = 0; i < CELL_MAX_PADS; ++i) { - if (i >= config.max_connect) + if (i >= config.get_max_connect()) break; pads[i]->m_port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; // TODO: should ASSIGN flags be cleared here? @@ -657,7 +657,7 @@ error_code cellPadGetInfo2(vm::ptr info) std::memset(info.get_ptr(), 0, sizeof(CellPadInfo2)); const PadInfo& rinfo = handler->GetInfo(); - info->max_connect = config.max_connect; + info->max_connect = config.get_max_connect(); // Here it is forcibly clamped info->now_connect = rinfo.now_connect; info->system_info = rinfo.system_info; @@ -665,7 +665,7 @@ error_code cellPadGetInfo2(vm::ptr info) for (u32 i = 0; i < CELL_PAD_MAX_PORT_NUM; ++i) { - if (i >= config.max_connect) + if (i >= config.get_max_connect()) break; info->port_status[i] = pads[i]->m_port_status; @@ -696,7 +696,7 @@ error_code cellPadGetCapabilityInfo(u32 port_no, vm::ptr const auto& pads = handler->GetPads(); - if (port_no >= config.max_connect) + if (port_no >= config.get_max_connect()) return CELL_PAD_ERROR_NO_DEVICE; const auto& pad = pads[port_no]; @@ -754,7 +754,7 @@ error_code cellPadInfoPressMode(u32 port_no) const auto& pads = handler->GetPads(); - if (port_no >= config.max_connect) + if (port_no >= config.get_max_connect()) return CELL_PAD_ERROR_NO_DEVICE; const auto& pad = pads[port_no]; @@ -783,7 +783,7 @@ error_code cellPadInfoSensorMode(u32 port_no) const auto& pads = handler->GetPads(); - if (port_no >= config.max_connect) + if (port_no >= config.get_max_connect()) return CELL_PAD_ERROR_NO_DEVICE; const auto& pad = pads[port_no]; diff --git a/rpcs3/Emu/Cell/Modules/cellPad.h b/rpcs3/Emu/Cell/Modules/cellPad.h index 5b7c713b19..0b378773f9 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.h +++ b/rpcs3/Emu/Cell/Modules/cellPad.h @@ -198,6 +198,11 @@ struct pad_info pad_info() = default; pad_info(utils::serial& ar); void save(utils::serial& ar); + + u32 get_max_connect() const + { + return std::min(max_connect, CELL_PAD_MAX_PORT_NUM); + } }; error_code cellPadGetData(u32 port_no, vm::ptr data); From 4bbe885f35390194d0377195d9024cfb8499262b Mon Sep 17 00:00:00 2001 From: Eladash Date: Wed, 9 Aug 2023 11:11:59 +0300 Subject: [PATCH 065/184] LV2/cellPad: Implement priority-based connection updates --- rpcs3/Emu/Cell/Modules/cellKb.cpp | 12 +-- rpcs3/Emu/Cell/Modules/cellMouse.cpp | 12 +-- rpcs3/Emu/Cell/Modules/cellPad.cpp | 113 +++++++++++++++++++----- rpcs3/Emu/Cell/Modules/cellPad.h | 10 ++- rpcs3/Emu/Cell/Modules/sys_io_.cpp | 123 +++++++++++++++++++++------ rpcs3/Emu/Cell/lv2/sys_hid.cpp | 4 +- rpcs3/Emu/Cell/lv2/sys_hid.h | 2 +- rpcs3/Emu/Io/PadHandler.cpp | 18 +++- 8 files changed, 226 insertions(+), 68 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellKb.cpp b/rpcs3/Emu/Cell/Modules/cellKb.cpp index b90eef2f12..f6ae9433d2 100644 --- a/rpcs3/Emu/Cell/Modules/cellKb.cpp +++ b/rpcs3/Emu/Cell/Modules/cellKb.cpp @@ -6,8 +6,8 @@ #include "Emu/Io/KeyboardHandler.h" #include "cellKb.h" -extern void libio_sys_config_init(); -extern void libio_sys_config_end(); +error_code sys_config_start(ppu_thread& ppu); +error_code sys_config_stop(ppu_thread& ppu); extern bool is_input_allowed(); @@ -61,7 +61,7 @@ void KeyboardHandlerBase::save(utils::serial& ar) ar(inited ? m_info.max_connect : 0); } -error_code cellKbInit(u32 max_connect) +error_code cellKbInit(ppu_thread& ppu, u32 max_connect) { sys_io.warning("cellKbInit(max_connect=%d)", max_connect); @@ -78,13 +78,13 @@ error_code cellKbInit(u32 max_connect) return CELL_KB_ERROR_INVALID_PARAMETER; } - libio_sys_config_init(); + sys_config_start(ppu); handler.Init(std::min(max_connect, 7u)); return CELL_OK; } -error_code cellKbEnd() +error_code cellKbEnd(ppu_thread& ppu) { sys_io.notice("cellKbEnd()"); @@ -96,7 +96,7 @@ error_code cellKbEnd() return CELL_KB_ERROR_UNINITIALIZED; // TODO - libio_sys_config_end(); + sys_config_stop(ppu); return CELL_OK; } diff --git a/rpcs3/Emu/Cell/Modules/cellMouse.cpp b/rpcs3/Emu/Cell/Modules/cellMouse.cpp index 76437c2f46..f7ae70bfb4 100644 --- a/rpcs3/Emu/Cell/Modules/cellMouse.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMouse.cpp @@ -7,8 +7,8 @@ #include "cellMouse.h" -extern void libio_sys_config_init(); -extern void libio_sys_config_end(); +error_code sys_config_start(ppu_thread& ppu); +error_code sys_config_stop(ppu_thread& ppu); extern bool is_input_allowed(); @@ -61,7 +61,7 @@ void MouseHandlerBase::save(utils::serial& ar) ar(inited ? m_info.max_connect : 0); } -error_code cellMouseInit(u32 max_connect) +error_code cellMouseInit(ppu_thread& ppu, u32 max_connect) { sys_io.notice("cellMouseInit(max_connect=%d)", max_connect); @@ -78,7 +78,7 @@ error_code cellMouseInit(u32 max_connect) return CELL_MOUSE_ERROR_INVALID_PARAMETER; } - libio_sys_config_init(); + sys_config_start(ppu); handler.Init(std::min(max_connect, 7u)); return CELL_OK; @@ -121,7 +121,7 @@ error_code cellMouseClearBuf(u32 port_no) return CELL_OK; } -error_code cellMouseEnd() +error_code cellMouseEnd(ppu_thread& ppu) { sys_io.notice("cellMouseEnd()"); @@ -133,7 +133,7 @@ error_code cellMouseEnd() return CELL_MOUSE_ERROR_UNINITIALIZED; // TODO - libio_sys_config_end(); + sys_config_stop(ppu); return CELL_OK; } diff --git a/rpcs3/Emu/Cell/Modules/cellPad.cpp b/rpcs3/Emu/Cell/Modules/cellPad.cpp index 075d5dbb4e..71a1c54eef 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPad.cpp @@ -3,13 +3,14 @@ #include "Emu/system_config.h" #include "Emu/Cell/PPUModule.h" #include "Emu/Cell/lv2/sys_process.h" +#include "Emu/Cell/lv2/sys_sync.h" #include "Emu/Io/pad_types.h" #include "Input/pad_thread.h" #include "Input/product_info.h" #include "cellPad.h" -extern void libio_sys_config_init(); -extern void libio_sys_config_end(); +error_code sys_config_start(ppu_thread& ppu); +error_code sys_config_stop(ppu_thread& ppu); extern bool is_input_allowed(); @@ -63,8 +64,40 @@ void pad_info::save(utils::serial& ar) ar(max_connect, port_setting); } +extern void send_sys_io_connect_event(u32 index, u32 state); -error_code cellPadInit(u32 max_connect) +void cellPad_NotifyStateChange(u32 index, u32 state) +{ + auto info = g_fxo->try_get(); + + if (!info) + { + return; + } + + std::lock_guard lock(pad::g_pad_mutex); + + if (!info->max_connect) + { + return; + } + + const u32 old = info->reported_statuses[index]; + + if (~(old ^ state) & CELL_PAD_STATUS_CONNECTED) + { + return; + } + + info->reported_statuses[index] = (state & CELL_PAD_STATUS_CONNECTED) | CELL_PAD_STATUS_ASSIGN_CHANGES; +} + +extern void pad_state_notify_state_change(u32 index, u32 state) +{ + cellPad_NotifyStateChange(index, state); +} + +error_code cellPadInit(ppu_thread& ppu, u32 max_connect) { sys_io.warning("cellPadInit(max_connect=%d)", max_connect); @@ -78,13 +111,33 @@ error_code cellPadInit(u32 max_connect) if (max_connect == 0 || max_connect > CELL_MAX_PADS) return CELL_PAD_ERROR_INVALID_PARAMETER; - libio_sys_config_init(); + sys_config_start(ppu); + config.max_connect = max_connect; config.port_setting.fill(CELL_PAD_SETTING_PRESS_OFF | CELL_PAD_SETTING_SENSOR_OFF); + config.reported_statuses = {}; + + std::array statuses{}; + + const auto handler = pad::get_current_handler(); + + const auto& pads = handler->GetPads(); + + for (u32 i = 0; i < statuses.size(); ++i) + { + if (i >= config.get_max_connect()) + break; + + if (pads[i]->m_port_status & CELL_PAD_STATUS_CONNECTED) + { + send_sys_io_connect_event(i, CELL_PAD_STATUS_CONNECTED); + } + } + return CELL_OK; } -error_code cellPadEnd() +error_code cellPadEnd(ppu_thread& ppu) { sys_io.notice("cellPadEnd()"); @@ -95,7 +148,7 @@ error_code cellPadEnd() if (!config.max_connect.exchange(0)) return CELL_PAD_ERROR_UNINITIALIZED; - libio_sys_config_end(); + sys_config_stop(ppu); return CELL_OK; } @@ -143,7 +196,7 @@ error_code cellPadClearBuf(u32 port_no) const auto& pad = pads[port_no]; - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); clear_pad_buffer(pad); @@ -176,7 +229,7 @@ error_code cellPadGetData(u32 port_no, vm::ptr data) const auto& pad = pads[port_no]; - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); pad_get_data(port_no, data.get_ptr()); @@ -452,7 +505,7 @@ error_code cellPadPeriphGetData(u32 port_no, vm::ptr data) const auto& pad = pads[port_no]; - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); pad_get_data(port_no, &data->cellpad_data); @@ -487,7 +540,7 @@ error_code cellPadGetRawData(u32 port_no, vm::ptr data) const auto& pad = pads[port_no]; - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); // ? @@ -553,7 +606,7 @@ error_code cellPadSetActDirect(u32 port_no, vm::ptr param) const auto& pad = pads[port_no]; - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); // TODO: find out if this is checked here or later or at all @@ -585,9 +638,10 @@ error_code cellPadGetInfo(vm::ptr info) const PadInfo& rinfo = handler->GetInfo(); info->max_connect = config.max_connect; - info->now_connect = rinfo.now_connect; info->system_info = rinfo.system_info; + u32 now_connect = 0; + const auto& pads = handler->GetPads(); for (u32 i = 0; i < CELL_MAX_PADS; ++i) @@ -595,8 +649,16 @@ error_code cellPadGetInfo(vm::ptr info) if (i >= config.get_max_connect()) break; - pads[i]->m_port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; // TODO: should ASSIGN flags be cleared here? - info->status[i] = pads[i]->m_port_status; + if (!config.is_reportedly_connected(i)) + continue; + + config.reported_statuses[i] &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; // TODO: should ASSIGN flags be cleared here? + info->status[i] = config.reported_statuses[i]; + + if (config.reported_statuses[i] & CELL_PAD_STATUS_CONNECTED) + { + now_connect++; + } if (pads[i]->m_vendor_id == 0 || pads[i]->m_product_id == 0) { @@ -635,6 +697,7 @@ error_code cellPadGetInfo(vm::ptr info) } } + info->now_connect = now_connect; return CELL_OK; } @@ -658,9 +721,10 @@ error_code cellPadGetInfo2(vm::ptr info) const PadInfo& rinfo = handler->GetInfo(); info->max_connect = config.get_max_connect(); // Here it is forcibly clamped - info->now_connect = rinfo.now_connect; info->system_info = rinfo.system_info; + u32 now_connect = 0; + const auto& pads = handler->GetPads(); for (u32 i = 0; i < CELL_PAD_MAX_PORT_NUM; ++i) @@ -668,13 +732,22 @@ error_code cellPadGetInfo2(vm::ptr info) if (i >= config.get_max_connect()) break; - info->port_status[i] = pads[i]->m_port_status; - pads[i]->m_port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; + if (!config.is_reportedly_connected(i)) + continue; + + info->port_status[i] = config.reported_statuses[i]; + config.reported_statuses[i] &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; info->port_setting[i] = config.port_setting[i]; info->device_capability[i] = pads[i]->m_device_capability; info->device_type[i] = pads[i]->m_device_type; + + if (config.reported_statuses[i] & CELL_PAD_STATUS_CONNECTED) + { + now_connect++; + } } + info->now_connect = now_connect; return CELL_OK; } @@ -701,7 +774,7 @@ error_code cellPadGetCapabilityInfo(u32 port_no, vm::ptr const auto& pad = pads[port_no]; - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); // Should return the same as device capability mask, psl1ght has it backwards in pad->h @@ -759,7 +832,7 @@ error_code cellPadInfoPressMode(u32 port_no) const auto& pad = pads[port_no]; - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); return not_an_error((pad->m_device_capability & CELL_PAD_CAPABILITY_PRESS_MODE) ? 1 : 0); @@ -788,7 +861,7 @@ error_code cellPadInfoSensorMode(u32 port_no) const auto& pad = pads[port_no]; - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); return not_an_error((pad->m_device_capability & CELL_PAD_CAPABILITY_SENSOR_MODE) ? 1 : 0); diff --git a/rpcs3/Emu/Cell/Modules/cellPad.h b/rpcs3/Emu/Cell/Modules/cellPad.h index 0b378773f9..f4603a80e6 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.h +++ b/rpcs3/Emu/Cell/Modules/cellPad.h @@ -192,6 +192,7 @@ struct pad_info { atomic_t max_connect = 0; std::array port_setting{ 0 }; + std::array reported_statuses{}; SAVESTATE_INIT_POS(11); @@ -203,8 +204,15 @@ struct pad_info { return std::min(max_connect, CELL_PAD_MAX_PORT_NUM); } + + // Unreliable way the firmware uses to optimize away pad calls for disconnected pads + // This result relies on data updates from config events on a dedicated thread to receive them + bool is_reportedly_connected(u32 port_no) const + { + return port_no < get_max_connect() && !!(reported_statuses[port_no] & CELL_PAD_STATUS_CONNECTED); + } }; error_code cellPadGetData(u32 port_no, vm::ptr data); -error_code cellPadInit(u32 max_connect); +error_code cellPadInit(ppu_thread& ppu, u32 max_connect); error_code cellPadSetPortSetting(u32 port_no, u32 port_setting); diff --git a/rpcs3/Emu/Cell/Modules/sys_io_.cpp b/rpcs3/Emu/Cell/Modules/sys_io_.cpp index abbdb53cd5..c9874f1ffa 100644 --- a/rpcs3/Emu/Cell/Modules/sys_io_.cpp +++ b/rpcs3/Emu/Cell/Modules/sys_io_.cpp @@ -3,6 +3,10 @@ #include "Emu/IdManager.h" #include "Emu/Cell/PPUModule.h" +#include "Emu/Cell/lv2/sys_event.h" +#include "Emu/Cell/lv2/sys_ppu_thread.h" +#include "Emu/Cell/Modules/sysPrxForUser.h" + LOG_CHANNEL(sys_io); extern void cellPad_init(); @@ -13,53 +17,114 @@ struct libio_sys_config { shared_mutex mtx; s32 init_ctr = 0; - u32 stack_addr = 0; + u32 ppu_id = 0; + u32 queue_id = 0; ~libio_sys_config() noexcept { - if (stack_addr) - { - ensure(vm::dealloc(stack_addr, vm::stack)); - } } }; -// Only exists internally (has no name) -extern void libio_sys_config_init() +extern void cellPad_NotifyStateChange(u32 index, u32 state); + +void config_event_entry(ppu_thread& ppu) { auto& cfg = g_fxo->get(); + // Ensure awake + ppu.check_state(); + + while (!sys_event_queue_receive(ppu, cfg.queue_id, vm::null, 0)) + { + if (ppu.is_stopped()) + { + return; + } + + // Some delay + thread_ctrl::wait_for(10000); + + // Wakeup + ppu.check_state(); + + const u64 arg1 = ppu.gpr[5]; + const u64 arg2 = ppu.gpr[6]; + const u64 arg3 = ppu.gpr[7]; + + // TODO: Reverse-engineer proper event system + + if (arg1 == 1) + { + cellPad_NotifyStateChange(arg2, arg3); + } + } + + ppu_execute<&sys_ppu_thread_exit>(ppu, 0); +} + +extern void send_sys_io_connect_event(u32 index, u32 state) +{ + auto& cfg = g_fxo->get(); + + std::lock_guard lock(cfg.mtx); + + if (cfg.init_ctr) + { + if (auto port = idm::get(cfg.queue_id)) + { + port->send(0, 1, index, state); + } + } +} + +error_code sys_config_start(ppu_thread& ppu) +{ + sys_io.warning("sys_config_start()"); + + auto& cfg = g_fxo->get(); + std::lock_guard lock(cfg.mtx); if (cfg.init_ctr++ == 0) { - // Belongs to "_cfg_evt_hndlr" thread (8k stack) - cfg.stack_addr = ensure(vm::alloc(0x2000, vm::stack, 4096)); + // Run thread + vm::var _tid; + vm::var queue_id; + vm::var _name = vm::make_str("_cfg_evt_hndlr"); + + vm::var attr; + attr->protocol = SYS_SYNC_PRIORITY; + attr->type = SYS_PPU_QUEUE; + attr->name_u64 = 0; + + ensure(CELL_OK == sys_event_queue_create(ppu, queue_id, attr, 0, 0x20)); + ensure(CELL_OK == ppu_execute<&sys_ppu_thread_create>(ppu, +_tid, g_fxo->get().func_addr(FIND_FUNC(config_event_entry)), 0, 512, 0x2000, SYS_PPU_THREAD_CREATE_JOINABLE, +_name)); + + cfg.ppu_id = static_cast(*_tid); + cfg.queue_id = *queue_id; } -} - -extern void libio_sys_config_end() -{ - auto& cfg = g_fxo->get(); - - std::lock_guard lock(cfg.mtx); - - if (cfg.init_ctr-- == 1) - { - ensure(vm::dealloc(std::exchange(cfg.stack_addr, 0), vm::stack)); - } -} - -error_code sys_config_start() -{ - sys_io.todo("sys_config_start()"); return CELL_OK; } -error_code sys_config_stop() +error_code sys_config_stop(ppu_thread& ppu) { - sys_io.todo("sys_config_stop()"); + sys_io.warning("sys_config_stop()"); + + auto& cfg = g_fxo->get(); + + std::lock_guard lock(cfg.mtx); + + if (cfg.init_ctr && cfg.init_ctr-- == 1) + { + ensure(CELL_OK == sys_event_queue_destroy(ppu, cfg.queue_id, SYS_EVENT_QUEUE_DESTROY_FORCE)); + ensure(CELL_OK == sys_ppu_thread_join(ppu, cfg.ppu_id, +vm::var{})); + } + else + { + // TODO: Unknown error + } + return CELL_OK; } @@ -114,4 +179,6 @@ DECLARE(ppu_module_manager::sys_io)("sys_io", []() REG_FUNC(sys_io, sys_config_register_service); REG_FUNC(sys_io, sys_config_unregister_io_error_handler); REG_FUNC(sys_io, sys_config_unregister_service); + + REG_HIDDEN_FUNC(config_event_entry); }); diff --git a/rpcs3/Emu/Cell/lv2/sys_hid.cpp b/rpcs3/Emu/Cell/lv2/sys_hid.cpp index 0f297eaafb..e5b5a310aa 100644 --- a/rpcs3/Emu/Cell/lv2/sys_hid.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_hid.cpp @@ -12,7 +12,7 @@ LOG_CHANNEL(sys_hid); -error_code sys_hid_manager_open(u64 device_type, u64 port_no, vm::ptr handle) +error_code sys_hid_manager_open(ppu_thread& ppu, u64 device_type, u64 port_no, vm::ptr handle) { sys_hid.todo("sys_hid_manager_open(device_type=0x%llx, port_no=0x%llx, handle=*0x%llx)", device_type, port_no, handle); @@ -34,7 +34,7 @@ error_code sys_hid_manager_open(u64 device_type, u64 port_no, vm::ptr handl if (device_type == 1) { - cellPadInit(7); + cellPadInit(ppu, 7); cellPadSetPortSetting(::narrow(port_no) /* 0 */, CELL_PAD_SETTING_LDD | CELL_PAD_SETTING_PRESS_ON | CELL_PAD_SETTING_SENSOR_ON); } diff --git a/rpcs3/Emu/Cell/lv2/sys_hid.h b/rpcs3/Emu/Cell/lv2/sys_hid.h index c1c6754a94..66acb78006 100644 --- a/rpcs3/Emu/Cell/lv2/sys_hid.h +++ b/rpcs3/Emu/Cell/lv2/sys_hid.h @@ -34,7 +34,7 @@ struct sys_hid_manager_514_pkg_d // SysCalls -error_code sys_hid_manager_open(u64 device_type, u64 port_no, vm::ptr handle); +error_code sys_hid_manager_open(ppu_thread& ppu, u64 device_type, u64 port_no, vm::ptr handle); error_code sys_hid_manager_ioctl(u32 hid_handle, u32 pkg_id, vm::ptr buf, u64 buf_size); error_code sys_hid_manager_add_hot_key_observer(u32 event_queue, vm::ptr unk); error_code sys_hid_manager_check_focus(); diff --git a/rpcs3/Emu/Io/PadHandler.cpp b/rpcs3/Emu/Io/PadHandler.cpp index 4f98110a87..40eb6dc000 100644 --- a/rpcs3/Emu/Io/PadHandler.cpp +++ b/rpcs3/Emu/Io/PadHandler.cpp @@ -7,6 +7,8 @@ cfg_input g_cfg_input; +extern void pad_state_notify_state_change(u32 index, u32 state); + PadHandlerBase::PadHandlerBase(pad_handler type) : m_type(type) { } @@ -701,8 +703,10 @@ void PadHandlerBase::process() if (!last_connection_status[i]) { input_log.success("%s device %d connected", m_type, i); - pad->m_port_status |= CELL_PAD_STATUS_CONNECTED; - pad->m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES; + + pad->m_port_status |= CELL_PAD_STATUS_CONNECTED + CELL_PAD_STATUS_ASSIGN_CHANGES; + pad_state_notify_state_change(i, CELL_PAD_STATUS_CONNECTED); + last_connection_status[i] = true; connected_devices++; } @@ -723,8 +727,10 @@ void PadHandlerBase::process() if (!last_connection_status[i]) { input_log.success("%s device %d connected by force", m_type, i); - pad->m_port_status |= CELL_PAD_STATUS_CONNECTED; - pad->m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES; + + pad->m_port_status |= CELL_PAD_STATUS_CONNECTED + CELL_PAD_STATUS_ASSIGN_CHANGES; + pad_state_notify_state_change(i, CELL_PAD_STATUS_CONNECTED); + last_connection_status[i] = true; connected_devices++; } @@ -734,8 +740,12 @@ void PadHandlerBase::process() if (last_connection_status[i]) { input_log.error("%s device %d disconnected", m_type, i); + pad->m_port_status &= ~CELL_PAD_STATUS_CONNECTED; pad->m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES; + + pad_state_notify_state_change(i, CELL_PAD_STATUS_DISCONNECTED); + last_connection_status[i] = false; connected_devices--; } From eae1c5afdd414d9afbab2bccca35137cefddffc2 Mon Sep 17 00:00:00 2001 From: Eladash Date: Thu, 10 Aug 2023 22:04:15 +0300 Subject: [PATCH 066/184] PPU Loader: Fix main()'s envp --- rpcs3/Emu/Cell/PPUModule.cpp | 84 ++++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 28 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index 2d42accef6..123056fdf3 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -2307,33 +2307,6 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str } } - // Initialize process arguments - auto args = vm::ptr::make(vm::alloc(u32{sizeof(u64)} * (::size32(Emu.argv) + ::size32(Emu.envp) + 2), vm::main)); - auto argv = args; - - for (const auto& arg : Emu.argv) - { - const u32 arg_size = ::size32(arg) + 1; - const u32 arg_addr = vm::alloc(arg_size, vm::main); - - std::memcpy(vm::base(arg_addr), arg.data(), arg_size); - - *args++ = arg_addr; - } - - *args++ = 0; - auto envp = args; - - for (const auto& arg : Emu.envp) - { - const u32 arg_size = ::size32(arg) + 1; - const u32 arg_addr = vm::alloc(arg_size, vm::main); - - std::memcpy(vm::base(arg_addr), arg.data(), arg_size); - - *args++ = arg_addr; - } - // Fix primary stack size switch (u32 sz = primary_stacksize) { @@ -2367,6 +2340,61 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str ppu->gpr[1] -= Emu.data.size(); } + // Initialize process arguments + + // Calculate storage requirements on the stack + const u32 pointers_storage_size = u32{sizeof(u64)} * (::size32(Emu.envp) + ::size32(Emu.argv) + 3); + + u32 stack_alloc_size = pointers_storage_size; + + for (const auto& arg : Emu.argv) + { + stack_alloc_size += utils::align(::size32(arg) + 1, 0x10); + } + + for (const auto& arg : Emu.envp) + { + stack_alloc_size += utils::align(::size32(arg) + 1, 0x10); + } + + ensure(ppu->stack_size > stack_alloc_size); + + vm::ptr args = vm::cast(static_cast(ppu->stack_addr + ppu->stack_size - stack_alloc_size - utils::align(Emu.data.size(), 0x10))); + vm::ptr args_data = vm::cast(args.addr() + pointers_storage_size); + + const vm::ptr argv = args; + + for (const auto& arg : Emu.argv) + { + const u32 arg_size = ::size32(arg) + 1; + + std::memcpy(args_data.get_ptr(), arg.data(), arg_size); + + *args++ = args_data.addr(); + args_data = vm::cast(args_data.addr() + utils::align(arg_size, 0x10)); + } + + *args++ = 0; + + const vm::ptr envp = vm::cast(utils::align(args.addr(), 8)); + args = envp; + + for (const auto& arg : Emu.envp) + { + const u32 arg_size = ::size32(arg) + 1; + + std::memcpy(args_data.get_ptr(), arg.data(), arg_size); + + *args++ = args_data.addr(); + args_data = vm::cast(args_data.addr() + utils::align(arg_size, 0x10)); + } + + *args++ = 0; + + *args++ = 0; // Unknown + + ppu->gpr[1] -= stack_alloc_size; + ensure(g_fxo->get().take(primary_stacksize)); ppu->cmd_push({ppu_cmd::initialize, 0}); @@ -2400,7 +2428,7 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str // Set command line arguments, run entry function ppu->cmd_list ({ - { ppu_cmd::set_args, 8 }, u64{Emu.argv.size()}, u64{argv.addr()}, u64{envp.addr()}, u64{0}, u64{ppu->id}, u64{tls_vaddr}, u64{tls_fsize}, u64{tls_vsize}, + { ppu_cmd::set_args, 8 }, u64{Emu.argv.size()}, u64{argv.addr()}, u64{envp.addr()}, u64{Emu.envp.size()}, u64{ppu->id}, u64{tls_vaddr}, u64{tls_fsize}, u64{tls_vsize}, { ppu_cmd::set_gpr, 11 }, u64{elf.header.e_entry}, { ppu_cmd::set_gpr, 12 }, u64{malloc_pagesize}, { ppu_cmd::entry_call, 0 }, From c6dcf3f1d373daabc9121593afeb505f0af79832 Mon Sep 17 00:00:00 2001 From: Ivan Chikish Date: Sun, 6 Aug 2023 10:04:38 +0300 Subject: [PATCH 067/184] (Linux) Fixup futex_waitv --- rpcs3/util/atomic.cpp | 50 ++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/rpcs3/util/atomic.cpp b/rpcs3/util/atomic.cpp index f9541320e9..97f0ac35d0 100644 --- a/rpcs3/util/atomic.cpp +++ b/rpcs3/util/atomic.cpp @@ -1,7 +1,6 @@ #include "atomic.hpp" #if defined(__linux__) -// This definition is unused on Linux #define USE_FUTEX #elif !defined(_WIN32) #define USE_STD @@ -29,6 +28,21 @@ namespace utils #include "Utilities/sync.h" #include "Utilities/StrFmt.h" +#ifdef __linux__ +static bool has_waitv() +{ + static const bool s_has_waitv = [] + { + syscall(SYS_futex_waitv, 0, 0, 0, 0, 0); + if (errno == ENOSYS) + return false; + return true; + }(); + + return s_has_waitv; +} +#endif + #include #include #include @@ -843,7 +857,7 @@ atomic_wait_engine::wait(const void* data, u32 old_value, u64 timeout, atomic_wa ::timespec ts{}; if (timeout + 1) { - if (ext) [[unlikely]] + if (ext && ext->data) [[unlikely]] { // futex_waitv uses absolute timeout ::clock_gettime(CLOCK_MONOTONIC, &ts); @@ -851,7 +865,7 @@ atomic_wait_engine::wait(const void* data, u32 old_value, u64 timeout, atomic_wa ts.tv_sec += timeout / 1'000'000'000; ts.tv_nsec += timeout % 1'000'000'000; - if (ts.tv_nsec > 1'000'000'000) + if (ts.tv_nsec >= 1'000'000'000) { ts.tv_sec++; ts.tv_nsec -= 1'000'000'000; @@ -874,7 +888,7 @@ atomic_wait_engine::wait(const void* data, u32 old_value, u64 timeout, atomic_wa } } - if (ext_size) [[unlikely]] + if (ext_size && has_waitv()) [[unlikely]] { if (syscall(SYS_futex_waitv, +vec, ext_size + 1, 0, timeout + 1 ? &ts : nullptr, CLOCK_MONOTONIC) == -1) { @@ -887,8 +901,9 @@ atomic_wait_engine::wait(const void* data, u32 old_value, u64 timeout, atomic_wa fmt::throw_exception("futex_waitv: bad param"); } } + return; } - else + else if (has_waitv()) { if (futex(const_cast(data), FUTEX_WAIT_PRIVATE, old_value, timeout + 1 ? &ts : nullptr) == -1) { @@ -897,9 +912,8 @@ atomic_wait_engine::wait(const void* data, u32 old_value, u64 timeout, atomic_wa fmt::throw_exception("futex: bad param"); } } + return; } - - return; #endif if (!s_tls_wait_cb(data, 0, 0)) @@ -1242,8 +1256,14 @@ void atomic_wait_engine::notify_one(const void* data) s_tls_notify_cb(data, 0); #ifdef __linux__ - futex(const_cast(data), FUTEX_WAKE_PRIVATE, 1); -#else + if (has_waitv()) + { + futex(const_cast(data), FUTEX_WAKE_PRIVATE, 1); + if (s_tls_notify_cb) + s_tls_notify_cb(data, -1); + return; + } +#endif const uptr iptr = reinterpret_cast(data) & (~s_ref_mask >> 16); root_info::slot_search(iptr, [&](u32 cond_id) @@ -1255,7 +1275,6 @@ void atomic_wait_engine::notify_one(const void* data) return false; }); -#endif if (s_tls_notify_cb) s_tls_notify_cb(data, -1); @@ -1268,8 +1287,14 @@ atomic_wait_engine::notify_all(const void* data) s_tls_notify_cb(data, 0); #ifdef __linux__ - futex(const_cast(data), FUTEX_WAKE_PRIVATE, 1); -#else + if (has_waitv()) + { + futex(const_cast(data), FUTEX_WAKE_PRIVATE, INT_MAX); + if (s_tls_notify_cb) + s_tls_notify_cb(data, -1); + return; + } +#endif const uptr iptr = reinterpret_cast(data) & (~s_ref_mask >> 16); // Array count for batch notification @@ -1342,7 +1367,6 @@ atomic_wait_engine::notify_all(const void* data) { cond_free(~*(std::end(cond_ids) - i - 1)); } -#endif if (s_tls_notify_cb) s_tls_notify_cb(data, -1); From 5668b1bd7a08546ea1690d378198371c30e6d1d1 Mon Sep 17 00:00:00 2001 From: Elad Ashkenazi Date: Sat, 12 Aug 2023 02:03:24 +0300 Subject: [PATCH 068/184] PPU Loader: Fixup stack alignment after envp fix --- rpcs3/Emu/Cell/PPUModule.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index 123056fdf3..7a0b077d15 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -2337,13 +2337,13 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str if (!Emu.data.empty()) { std::memcpy(vm::base(ppu->stack_addr + ppu->stack_size - ::size32(Emu.data)), Emu.data.data(), Emu.data.size()); - ppu->gpr[1] -= Emu.data.size(); + ppu->gpr[1] -= utils::align(::size32(Emu.data), 0x10); } // Initialize process arguments // Calculate storage requirements on the stack - const u32 pointers_storage_size = u32{sizeof(u64)} * (::size32(Emu.envp) + ::size32(Emu.argv) + 3); + const u32 pointers_storage_size = u32{sizeof(u64)} * utils::align(::size32(Emu.envp) + ::size32(Emu.argv) + 2, 2); u32 stack_alloc_size = pointers_storage_size; @@ -2376,7 +2376,7 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str *args++ = 0; - const vm::ptr envp = vm::cast(utils::align(args.addr(), 8)); + const vm::ptr envp = args; args = envp; for (const auto& arg : Emu.envp) @@ -2391,8 +2391,6 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str *args++ = 0; - *args++ = 0; // Unknown - ppu->gpr[1] -= stack_alloc_size; ensure(g_fxo->get().take(primary_stacksize)); From 512f0a814c926320e20136e8c9ea74a36797d7a6 Mon Sep 17 00:00:00 2001 From: Malcolm Jestadt Date: Fri, 11 Aug 2023 18:34:50 -0400 Subject: [PATCH 069/184] SPU LLVM: Fix for AVX-512 CFLTU path - vcvvtps2udq doesn't turn negative numbers into 0, fix by using signed integer max with 0 instead of vrangeps --- rpcs3/Emu/Cell/SPURecompiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index 49ff59a856..67a8be2cb4 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -9882,7 +9882,7 @@ public: if (m_use_avx512) { - const auto sc = clamp_smax(a); + const auto sc = eval(bitcast(max(bitcast(a),splat(0x0)))); r.value = m_ir->CreateFPToUI(sc.value, get_type()); set_vr(op.rt, r); return; From d760e66fdb46939a17286588a25f4729e0849c60 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 12 Aug 2023 13:31:17 +0200 Subject: [PATCH 070/184] VS: Fix compilation on Visual Studio 17.7.0 --- Utilities/Config.h | 1 + rpcs3/Emu/RSX/Common/simple_array.hpp | 1 + 2 files changed, 2 insertions(+) diff --git a/Utilities/Config.h b/Utilities/Config.h index 361100dd7f..ee22a20d96 100644 --- a/Utilities/Config.h +++ b/Utilities/Config.h @@ -6,6 +6,7 @@ #include "util/atomic.hpp" #include "util/shared_ptr.hpp" +#include #include #include #include diff --git a/rpcs3/Emu/RSX/Common/simple_array.hpp b/rpcs3/Emu/RSX/Common/simple_array.hpp index 4082447deb..ce587911a8 100644 --- a/rpcs3/Emu/RSX/Common/simple_array.hpp +++ b/rpcs3/Emu/RSX/Common/simple_array.hpp @@ -2,6 +2,7 @@ #include #include +#include namespace rsx { From f199ad7a429d3ecaaf76f1a88ffec7cd62eaf84d Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 12 Aug 2023 13:54:10 +0200 Subject: [PATCH 071/184] patches: Improve location logging for invalid note sequence --- Utilities/bin_patch.cpp | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/Utilities/bin_patch.cpp b/Utilities/bin_patch.cpp index 2be0f84ce1..5d89beb79d 100644 --- a/Utilities/bin_patch.cpp +++ b/Utilities/bin_patch.cpp @@ -378,13 +378,21 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st { for (const auto note : notes_node) { - if (note && note.IsScalar()) + if (note) { - info.notes += note.Scalar(); + if (note.IsScalar()) + { + info.notes += note.Scalar(); + } + else + { + append_log_message(log_messages, fmt::format("Error: Skipping sequenced Note (patch: %s, key: %s, location: %s, file: %s)", description, main_key, get_yaml_node_location(note), path), &patch_log.error); + is_valid = false; + } } else { - append_log_message(log_messages, fmt::format("Error: Skipping sequenced Note (patch: %s, key: %s, location: %s, file: %s)", description, main_key, get_yaml_node_location(note), path), &patch_log.error); + append_log_message(log_messages, fmt::format("Error: Skipping sequenced Note (patch: %s, key: %s, location: %s, file: %s)", description, main_key, get_yaml_node_location(notes_node), path), &patch_log.error); is_valid = false; } } @@ -1752,9 +1760,9 @@ bool patch_engine::save_patches(const patch_map& patches, const std::string& pat { out << serial << YAML::BeginSeq; - for (const auto& app_version : app_versions) + for (const auto& [app_version, patch_config] : app_versions) { - out << app_version.first; + out << app_version; } out << YAML::EndSeq; @@ -1891,7 +1899,7 @@ patch_engine::patch_map patch_engine::load_config() for (const auto pair : root) { - const auto& hash = pair.first.Scalar(); + const std::string& hash = pair.first.Scalar(); if (const auto yml_type = pair.second.Type(); yml_type != YAML::NodeType::Map) { @@ -1901,7 +1909,7 @@ patch_engine::patch_map patch_engine::load_config() for (const auto patch : pair.second) { - const auto& description = patch.first.Scalar(); + const std::string& description = patch.first.Scalar(); if (const auto yml_type = patch.second.Type(); yml_type != YAML::NodeType::Map) { @@ -1911,7 +1919,7 @@ patch_engine::patch_map patch_engine::load_config() for (const auto title_node : patch.second) { - const auto& title = title_node.first.Scalar(); + const std::string& title = title_node.first.Scalar(); if (const auto yml_type = title_node.second.Type(); yml_type != YAML::NodeType::Map) { @@ -1921,7 +1929,7 @@ patch_engine::patch_map patch_engine::load_config() for (const auto serial_node : title_node.second) { - const auto& serial = serial_node.first.Scalar(); + const std::string& serial = serial_node.first.Scalar(); if (const auto yml_type = serial_node.second.Type(); yml_type != YAML::NodeType::Map) { From f2e782f5dd9a277926ba9e8a4f384a1472b78448 Mon Sep 17 00:00:00 2001 From: Malcolm Jestadt Date: Fri, 11 Aug 2023 23:33:15 -0400 Subject: [PATCH 072/184] SPU LLVM: Inline timer reads for WrDec and RdDec - Uses RDTSC to emulate the spu decrementer --- rpcs3/Emu/Cell/SPURecompiler.cpp | 33 +++++++++++++++++++++++++++++++- rpcs3/Emu/system_config.h | 2 +- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index 67a8be2cb4..dc5ddc657d 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -6521,6 +6521,24 @@ public: } case SPU_RdDec: { + if (utils::get_tsc_freq() && !(g_cfg.core.spu_loop_detection) && (g_cfg.core.clocks_scale == 100)) + { + const auto timestamp = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::ch_dec_start_timestamp)); + const auto dec_value = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::ch_dec_value)); + const auto tsc = m_ir->CreateCall(get_intrinsic(llvm::Intrinsic::x86_rdtsc)); + const auto tscx = m_ir->CreateMul(m_ir->CreateUDiv(tsc, m_ir->getInt64(utils::get_tsc_freq())), m_ir->getInt64(80000000)); + const auto tscm = m_ir->CreateUDiv(m_ir->CreateMul(m_ir->CreateURem(tsc, m_ir->getInt64(utils::get_tsc_freq())), m_ir->getInt64(80000000)), m_ir->getInt64(utils::get_tsc_freq())); + const auto tsctb = m_ir->CreateAdd(tscx, tscm); + + const auto frz = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::is_dec_frozen)); + const auto frzev = m_ir->CreateICmpEQ(frz, m_ir->getInt8(0)); + + const auto delta = m_ir->CreateTrunc(m_ir->CreateSub(tsctb, timestamp), get_type()); + const auto deltax = m_ir->CreateSelect(frzev, delta, m_ir->getInt32(0)); + res.value = m_ir->CreateSub(dec_value, deltax); + break; + } + res.value = call("spu_read_decrementer", &exec_read_dec, m_thread); break; } @@ -7188,7 +7206,20 @@ public: case SPU_WrDec: { call("spu_get_events", &exec_get_events, m_thread, m_ir->getInt32(SPU_EVENT_TM)); - m_ir->CreateStore(call("get_timebased_time", &get_timebased_time), spu_ptr(&spu_thread::ch_dec_start_timestamp)); + + if (utils::get_tsc_freq() && !(g_cfg.core.spu_loop_detection) && (g_cfg.core.clocks_scale == 100)) + { + const auto tsc = m_ir->CreateCall(get_intrinsic(llvm::Intrinsic::x86_rdtsc)); + const auto tscx = m_ir->CreateMul(m_ir->CreateUDiv(tsc, m_ir->getInt64(utils::get_tsc_freq())), m_ir->getInt64(80000000)); + const auto tscm = m_ir->CreateUDiv(m_ir->CreateMul(m_ir->CreateURem(tsc, m_ir->getInt64(utils::get_tsc_freq())), m_ir->getInt64(80000000)), m_ir->getInt64(utils::get_tsc_freq())); + const auto tsctb = m_ir->CreateAdd(tscx, tscm); + m_ir->CreateStore(tsctb, spu_ptr(&spu_thread::ch_dec_start_timestamp)); + } + else + { + m_ir->CreateStore(call("get_timebased_time", &get_timebased_time), spu_ptr(&spu_thread::ch_dec_start_timestamp)); + } + m_ir->CreateStore(val.value, spu_ptr(&spu_thread::ch_dec_value)); m_ir->CreateStore(m_ir->getInt8(0), spu_ptr(&spu_thread::is_dec_frozen)); return; diff --git a/rpcs3/Emu/system_config.h b/rpcs3/Emu/system_config.h index bdce5d0db2..3355c4d452 100644 --- a/rpcs3/Emu/system_config.h +++ b/rpcs3/Emu/system_config.h @@ -38,7 +38,7 @@ struct cfg_root : cfg::node cfg::_bool mfc_debug{ this, "MFC Debug" }; cfg::_int<0, 6> preferred_spu_threads{ this, "Preferred SPU Threads", 0, true }; // Number of hardware threads dedicated to heavy simultaneous spu tasks cfg::_int<0, 16> spu_delay_penalty{ this, "SPU delay penalty", 3 }; // Number of milliseconds to block a thread if a virtual 'core' isn't free - cfg::_bool spu_loop_detection{ this, "SPU loop detection", false, true }; // Try to detect wait loops and trigger thread yield + cfg::_bool spu_loop_detection{ this, "SPU loop detection", false }; // Try to detect wait loops and trigger thread yield cfg::_int<0, 6> max_spurs_threads{ this, "Max SPURS Threads", 6 }; // HACK. If less then 6, max number of running SPURS threads in each thread group. cfg::_enum spu_block_size{ this, "SPU Block Size", spu_block_size_type::safe }; cfg::_bool spu_accurate_getllar{ this, "Accurate GETLLAR", false, true }; From f40a6d496a1a06e93a23df7e09367ef186afaaa5 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 12 Aug 2023 23:20:11 +0200 Subject: [PATCH 073/184] home menu: remove non-dynamic SPU Loop Detection --- rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_settings.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_settings.cpp b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_settings.cpp index 36f36c355c..b398ecf3b7 100644 --- a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_settings.cpp +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_settings.cpp @@ -59,7 +59,6 @@ namespace rsx home_menu_settings_advanced::home_menu_settings_advanced(u16 x, u16 y, u16 width, u16 height, bool use_separators, home_menu_page* parent) : home_menu_settings_page(x, y, width, height, use_separators, parent, get_localized_string(localized_string_id::HOME_MENU_SETTINGS_ADVANCED)) { - add_checkbox(&g_cfg.core.spu_loop_detection, "SPU Loop Detection"); add_signed_slider(&g_cfg.core.preferred_spu_threads, "Preferred SPU Threads", "", 1); add_unsigned_slider(&g_cfg.core.max_cpu_preempt_count_per_frame, "Max Power Saving CPU-Preemptions", "", 1); add_checkbox(&g_cfg.core.rsx_accurate_res_access, "Accurate RSX reservation access"); From 39bbf17cafd4c9550c078bd72a3d47885b85f554 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 12 Aug 2023 23:06:22 +0200 Subject: [PATCH 074/184] cellRec: fix width of encoder frames Turns out the pitch was accidentally used as width, leading to an out of bounds read/write. I kept the pitch in the struct for completeness' sake. It may be needed later, if only for error checks. --- rpcs3/Emu/Cell/Modules/cellRec.cpp | 8 ++++---- rpcs3/Emu/RSX/GL/GLPresent.cpp | 2 +- rpcs3/Emu/RSX/GSFrameBase.h | 4 ++-- rpcs3/Emu/RSX/VK/VKPresent.cpp | 2 +- rpcs3/rpcs3qt/camera_settings_dialog.cpp | 1 + rpcs3/rpcs3qt/gs_frame.cpp | 6 +++--- rpcs3/rpcs3qt/gs_frame.h | 4 ++-- rpcs3/util/image_sink.h | 7 ++++--- rpcs3/util/media_utils.cpp | 4 ++-- rpcs3/util/media_utils.h | 2 +- rpcs3/util/video_provider.cpp | 4 ++-- rpcs3/util/video_provider.h | 2 +- 12 files changed, 24 insertions(+), 22 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellRec.cpp b/rpcs3/Emu/Cell/Modules/cellRec.cpp index 77064e248f..f3e4e7ea95 100644 --- a/rpcs3/Emu/Cell/Modules/cellRec.cpp +++ b/rpcs3/Emu/Cell/Modules/cellRec.cpp @@ -158,14 +158,14 @@ public: has_error = false; } - void add_frame(std::vector& frame, const u32 width, const u32 height, s32 pixel_format, usz timestamp_ms) override + void add_frame(std::vector& frame, u32 pitch, u32 width, u32 height, s32 pixel_format, usz timestamp_ms) override { std::lock_guard lock(m_mtx); if (m_flush) return; - m_frames_to_encode.emplace_back(timestamp_ms, width, height, pixel_format, std::move(frame)); + m_frames_to_encode.emplace_back(timestamp_ms, pitch, width, height, pixel_format, std::move(frame)); } encoder_frame get_frame() @@ -587,7 +587,7 @@ void rec_info::start_image_provider() { std::vector frame(frame_size); std::memcpy(frame.data(), video_input_buffer.get_ptr(), frame.size()); - encoder->add_frame(frame, input_format.pitch, input_format.height, input_format.av_pixel_format, timestamp_ms); + encoder->add_frame(frame, input_format.pitch, input_format.width, input_format.height, input_format.av_pixel_format, timestamp_ms); } } @@ -680,7 +680,7 @@ void rec_info::stop_image_provider(bool flush) { const usz pos = (start_offset + i) % video_ringbuffer.size(); utils::image_sink::encoder_frame& frame_data = video_ringbuffer[pos]; - encoder->add_frame(frame_data.data, frame_data.width, frame_data.height, frame_data.av_pixel_format, encoder->get_timestamp_ms(frame_data.pts - start_pts)); + encoder->add_frame(frame_data.data, frame_data.pitch, frame_data.width, frame_data.height, frame_data.av_pixel_format, encoder->get_timestamp_ms(frame_data.pts - start_pts)); // TODO: add audio data to encoder } diff --git a/rpcs3/Emu/RSX/GL/GLPresent.cpp b/rpcs3/Emu/RSX/GL/GLPresent.cpp index b99a230c2a..5fdedf5ee8 100644 --- a/rpcs3/Emu/RSX/GL/GLPresent.cpp +++ b/rpcs3/Emu/RSX/GL/GLPresent.cpp @@ -259,7 +259,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info) } else { - m_frame->present_frame(sshot_frame, buffer_width, buffer_height, false); + m_frame->present_frame(sshot_frame, buffer_width * 4, buffer_width, buffer_height, false); } } diff --git a/rpcs3/Emu/RSX/GSFrameBase.h b/rpcs3/Emu/RSX/GSFrameBase.h index 680445dd6b..e81aa55253 100644 --- a/rpcs3/Emu/RSX/GSFrameBase.h +++ b/rpcs3/Emu/RSX/GSFrameBase.h @@ -30,6 +30,6 @@ public: virtual display_handle_t handle() const = 0; virtual bool can_consume_frame() const = 0; - virtual void present_frame(std::vector& data, const u32 width, const u32 height, bool is_bgra) const = 0; - virtual void take_screenshot(const std::vector sshot_data, const u32 sshot_width, const u32 sshot_height, bool is_bgra) = 0; + virtual void present_frame(std::vector& data, u32 pitch, u32 width, u32 height, bool is_bgra) const = 0; + virtual void take_screenshot(const std::vector sshot_data, u32 sshot_width, u32 sshot_height, bool is_bgra) = 0; }; diff --git a/rpcs3/Emu/RSX/VK/VKPresent.cpp b/rpcs3/Emu/RSX/VK/VKPresent.cpp index 9ff2dee4f7..2f69505b96 100644 --- a/rpcs3/Emu/RSX/VK/VKPresent.cpp +++ b/rpcs3/Emu/RSX/VK/VKPresent.cpp @@ -712,7 +712,7 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) } else { - m_frame->present_frame(sshot_frame, buffer_width, buffer_height, is_bgra); + m_frame->present_frame(sshot_frame, buffer_width * 4, buffer_width, buffer_height, is_bgra); } } } diff --git a/rpcs3/rpcs3qt/camera_settings_dialog.cpp b/rpcs3/rpcs3qt/camera_settings_dialog.cpp index 89f72cef8d..3e2ae3caab 100644 --- a/rpcs3/rpcs3qt/camera_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/camera_settings_dialog.cpp @@ -66,6 +66,7 @@ camera_settings_dialog::camera_settings_dialog(QWidget* parent) { if (camera_info.isNull()) continue; ui->combo_camera->addItem(camera_info.description(), QVariant::fromValue(camera_info)); + camera_log.notice("Found camera: '%s'", camera_info.description()); } connect(ui->combo_camera, QOverload::of(&QComboBox::currentIndexChanged), this, &camera_settings_dialog::handle_camera_change); diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 44b927738a..dfc3d6917b 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -762,13 +762,13 @@ bool gs_frame::can_consume_frame() const return video_provider.can_consume_frame(); } -void gs_frame::present_frame(std::vector& data, const u32 width, const u32 height, bool is_bgra) const +void gs_frame::present_frame(std::vector& data, u32 pitch, u32 width, u32 height, bool is_bgra) const { utils::video_provider& video_provider = g_fxo->get(); - video_provider.present_frame(data, width, height, is_bgra); + video_provider.present_frame(data, pitch, width, height, is_bgra); } -void gs_frame::take_screenshot(std::vector data, const u32 sshot_width, const u32 sshot_height, bool is_bgra) +void gs_frame::take_screenshot(std::vector data, u32 sshot_width, u32 sshot_height, bool is_bgra) { std::thread( [sshot_width, sshot_height, is_bgra](std::vector sshot_data) diff --git a/rpcs3/rpcs3qt/gs_frame.h b/rpcs3/rpcs3qt/gs_frame.h index b39c790e01..4872de1dad 100644 --- a/rpcs3/rpcs3qt/gs_frame.h +++ b/rpcs3/rpcs3qt/gs_frame.h @@ -69,8 +69,8 @@ public: bool get_mouse_lock_state(); bool can_consume_frame() const override; - void present_frame(std::vector& data, const u32 width, const u32 height, bool is_bgra) const override; - void take_screenshot(std::vector data, const u32 sshot_width, const u32 sshot_height, bool is_bgra) override; + void present_frame(std::vector& data, u32 pitch, u32 width, u32 height, bool is_bgra) const override; + void take_screenshot(std::vector data, u32 sshot_width, u32 sshot_height, bool is_bgra) override; protected: void paintEvent(QPaintEvent *event) override; diff --git a/rpcs3/util/image_sink.h b/rpcs3/util/image_sink.h index 831232fa30..3c23eca514 100644 --- a/rpcs3/util/image_sink.h +++ b/rpcs3/util/image_sink.h @@ -15,7 +15,7 @@ namespace utils image_sink() = default; virtual void stop(bool flush = true) = 0; - virtual void add_frame(std::vector& frame, const u32 width, const u32 height, s32 pixel_format, usz timestamp_ms) = 0; + virtual void add_frame(std::vector& frame, u32 pitch, u32 width, u32 height, s32 pixel_format, usz timestamp_ms) = 0; s64 get_pts(usz timestamp_ms) const { @@ -32,12 +32,13 @@ namespace utils struct encoder_frame { encoder_frame() = default; - encoder_frame(usz timestamp_ms, u32 width, u32 height, s32 av_pixel_format, std::vector&& data) - : timestamp_ms(timestamp_ms), width(width), height(height), av_pixel_format(av_pixel_format), data(std::move(data)) + encoder_frame(usz timestamp_ms, u32 pitch, u32 width, u32 height, s32 av_pixel_format, std::vector&& data) + : timestamp_ms(timestamp_ms), pitch(pitch), width(width), height(height), av_pixel_format(av_pixel_format), data(std::move(data)) {} s64 pts = -1; // Optional usz timestamp_ms = 0; + u32 pitch = 0; u32 width = 0; u32 height = 0; s32 av_pixel_format = 0; // NOTE: Make sure this is a valid AVPixelFormat diff --git a/rpcs3/util/media_utils.cpp b/rpcs3/util/media_utils.cpp index 83eacc0b51..3dddd5b0f8 100644 --- a/rpcs3/util/media_utils.cpp +++ b/rpcs3/util/media_utils.cpp @@ -604,14 +604,14 @@ namespace utils m_audio_codec_id = codec_id; } - void video_encoder::add_frame(std::vector& frame, const u32 width, const u32 height, s32 pixel_format, usz timestamp_ms) + void video_encoder::add_frame(std::vector& frame, u32 pitch, u32 width, u32 height, s32 pixel_format, usz timestamp_ms) { // Do not allow new frames while flushing if (m_flush) return; std::lock_guard lock(m_mtx); - m_frames_to_encode.emplace_back(timestamp_ms, width, height, pixel_format, std::move(frame)); + m_frames_to_encode.emplace_back(timestamp_ms, pitch, width, height, pixel_format, std::move(frame)); } void video_encoder::pause(bool flush) diff --git a/rpcs3/util/media_utils.h b/rpcs3/util/media_utils.h index 5c25d14be9..2718a80617 100644 --- a/rpcs3/util/media_utils.h +++ b/rpcs3/util/media_utils.h @@ -120,7 +120,7 @@ namespace utils void set_sample_rate(u32 sample_rate); void set_audio_bitrate(u32 bitrate); void set_audio_codec(s32 codec_id); - void add_frame(std::vector& frame, const u32 width, const u32 height, s32 pixel_format, usz timestamp_ms) override; + void add_frame(std::vector& frame, u32 pitch, u32 width, u32 height, s32 pixel_format, usz timestamp_ms) override; void pause(bool flush = true); void stop(bool flush = true) override; void encode(); diff --git a/rpcs3/util/video_provider.cpp b/rpcs3/util/video_provider.cpp index cf3e910f93..d919137733 100644 --- a/rpcs3/util/video_provider.cpp +++ b/rpcs3/util/video_provider.cpp @@ -92,7 +92,7 @@ namespace utils return pts > m_last_pts_incoming; } - void video_provider::present_frame(std::vector& data, const u32 width, const u32 height, bool is_bgra) + void video_provider::present_frame(std::vector& data, u32 pitch, u32 width, u32 height, bool is_bgra) { std::lock_guard lock(m_mutex); @@ -132,6 +132,6 @@ namespace utils m_last_pts_incoming = pts; m_current_encoder_frame++; - m_image_sink->add_frame(data, width, height, is_bgra ? AVPixelFormat::AV_PIX_FMT_BGRA : AVPixelFormat::AV_PIX_FMT_RGBA, timestamp_ms); + m_image_sink->add_frame(data, pitch, width, height, is_bgra ? AVPixelFormat::AV_PIX_FMT_BGRA : AVPixelFormat::AV_PIX_FMT_RGBA, timestamp_ms); } } diff --git a/rpcs3/util/video_provider.h b/rpcs3/util/video_provider.h index 8fd4e12483..31a051a112 100644 --- a/rpcs3/util/video_provider.h +++ b/rpcs3/util/video_provider.h @@ -20,7 +20,7 @@ namespace utils bool set_image_sink(std::shared_ptr sink, recording_mode type); void set_pause_time(usz pause_time_ms); bool can_consume_frame(); - void present_frame(std::vector& data, const u32 width, const u32 height, bool is_bgra); + void present_frame(std::vector& data, u32 pitch, u32 width, u32 height, bool is_bgra); private: recording_mode m_type = recording_mode::stopped; From 219ee76bf23972de7e8452bf9a6b496300b4e2ca Mon Sep 17 00:00:00 2001 From: Elad Ashkenazi Date: Sun, 13 Aug 2023 13:52:15 +0300 Subject: [PATCH 075/184] cellPad: Fixup sys_config management --- rpcs3/Emu/Cell/Modules/sys_io_.cpp | 37 ++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/sys_io_.cpp b/rpcs3/Emu/Cell/Modules/sys_io_.cpp index c9874f1ffa..24cccd5022 100644 --- a/rpcs3/Emu/Cell/Modules/sys_io_.cpp +++ b/rpcs3/Emu/Cell/Modules/sys_io_.cpp @@ -62,11 +62,36 @@ void config_event_entry(ppu_thread& ppu) ppu_execute<&sys_ppu_thread_exit>(ppu, 0); } +std::unique_lock lock_lv2_mutex_alike(shared_mutex& mtx, ppu_thread* ppu) +{ + std::unique_lock lock(mtx, std::defer_lock); + + while (!lock.try_lock()) + { + if (ppu) + { + // Could not be acquired, put PPU to sleep + lv2_obj::sleep(*ppu); + } + + // Wait for unlock without owning the lock + mtx.lock_unlock(); + + if (ppu) + { + // Awake, still not owning + ppu->check_state(); + } + } + + return lock; +} + extern void send_sys_io_connect_event(u32 index, u32 state) { auto& cfg = g_fxo->get(); - std::lock_guard lock(cfg.mtx); + auto lock = lock_lv2_mutex_alike(cfg.mtx, cpu_thread::get_current()); if (cfg.init_ctr) { @@ -83,7 +108,7 @@ error_code sys_config_start(ppu_thread& ppu) auto& cfg = g_fxo->get(); - std::lock_guard lock(cfg.mtx); + auto lock = lock_lv2_mutex_alike(cfg.mtx, &ppu); if (cfg.init_ctr++ == 0) { @@ -98,10 +123,13 @@ error_code sys_config_start(ppu_thread& ppu) attr->name_u64 = 0; ensure(CELL_OK == sys_event_queue_create(ppu, queue_id, attr, 0, 0x20)); + ppu.check_state(); + cfg.queue_id = *queue_id; + ensure(CELL_OK == ppu_execute<&sys_ppu_thread_create>(ppu, +_tid, g_fxo->get().func_addr(FIND_FUNC(config_event_entry)), 0, 512, 0x2000, SYS_PPU_THREAD_CREATE_JOINABLE, +_name)); + ppu.check_state(); cfg.ppu_id = static_cast(*_tid); - cfg.queue_id = *queue_id; } return CELL_OK; @@ -113,11 +141,12 @@ error_code sys_config_stop(ppu_thread& ppu) auto& cfg = g_fxo->get(); - std::lock_guard lock(cfg.mtx); + auto lock = lock_lv2_mutex_alike(cfg.mtx, &ppu); if (cfg.init_ctr && cfg.init_ctr-- == 1) { ensure(CELL_OK == sys_event_queue_destroy(ppu, cfg.queue_id, SYS_EVENT_QUEUE_DESTROY_FORCE)); + ppu.check_state(); ensure(CELL_OK == sys_ppu_thread_join(ppu, cfg.ppu_id, +vm::var{})); } else From 0e23b89352b52c703a94a79c4ee22722870a1f5e Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sun, 13 Aug 2023 20:49:42 +0200 Subject: [PATCH 076/184] cellRec: add more encoder options --- 3rdparty/ffmpeg | 2 +- rpcs3/Emu/Cell/Modules/cellRec.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/3rdparty/ffmpeg b/3rdparty/ffmpeg index d9f2a87e81..4f8bcb1555 160000 --- a/3rdparty/ffmpeg +++ b/3rdparty/ffmpeg @@ -1 +1 @@ -Subproject commit d9f2a87e8112d1c1217adabb0dc945d8ad2da657 +Subproject commit 4f8bcb1555767522c2b21bfb08e5dcadad99ff62 diff --git a/rpcs3/Emu/Cell/Modules/cellRec.cpp b/rpcs3/Emu/Cell/Modules/cellRec.cpp index f3e4e7ea95..53165ef6a5 100644 --- a/rpcs3/Emu/Cell/Modules/cellRec.cpp +++ b/rpcs3/Emu/Cell/Modules/cellRec.cpp @@ -445,6 +445,7 @@ void rec_info::set_audio_params(s32 audio_format) case CELL_REC_PARAM_AUDIO_FMT_PCM_1536K: audio_codec_id = 65556; // AV_CODEC_ID_PCM_F32BE //audio_codec_id = 65557; // AV_CODEC_ID_PCM_F32LE // TODO: maybe this one? + break; default: audio_codec_id = 86018; // AV_CODEC_ID_AAC break; From 968762c135e51548ebd26776522b4c1f749a58d5 Mon Sep 17 00:00:00 2001 From: Elad Ashkenazi Date: Mon, 14 Aug 2023 17:00:46 +0300 Subject: [PATCH 077/184] Fix PPU SELF Precompilation --- rpcs3/Emu/Cell/PPUModule.cpp | 1 - rpcs3/Emu/Cell/PPUThread.cpp | 4 +++- rpcs3/Emu/System.cpp | 4 ++-- rpcs3/Emu/System.h | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index 7a0b077d15..ba4a063d55 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -2243,7 +2243,6 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str else { g_ps3_process_info = old_process_info; - Emu.ConfigurePPUCache(); } if (!load_libs.empty()) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index eb9f967fce..7ecb5d05b5 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -11,6 +11,7 @@ #include "Emu/Memory/vm_locking.h" #include "Emu/RSX/Core/RSXReservationLock.hpp" #include "Emu/VFS.h" +#include "Emu/vfs_config.h" #include "Emu/system_progress.hpp" #include "Emu/system_utils.hpp" #include "PPUThread.h" @@ -3574,7 +3575,7 @@ extern void ppu_precompile(std::vector& dir_queue, std::vector& dir_queue, std::vectorget(); _main.cache = rpcs3::utils::get_cache_dir(); - if (!m_title_id.empty() && m_cat != "1P") + if (with_title_id && !m_title_id.empty() && m_cat != "1P") { _main.cache += GetTitleID(); _main.cache += '/'; diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index 0148907b8a..7512b54047 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -353,7 +353,7 @@ public: std::string GetFormattedTitle(double fps) const; - void ConfigurePPUCache() const; + void ConfigurePPUCache(bool with_title_id = true) const; std::set GetGameDirs() const; void AddGamesFromDir(const std::string& path); From d25d5327e5cb1085431ea585a265ba5d0d61fedc Mon Sep 17 00:00:00 2001 From: oltolm Date: Thu, 27 Jul 2023 18:23:44 +0200 Subject: [PATCH 078/184] remove unnecessary defines --- rpcs3/util/vm_native.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/rpcs3/util/vm_native.cpp b/rpcs3/util/vm_native.cpp index 14bff5cdb4..6474ef68fd 100644 --- a/rpcs3/util/vm_native.cpp +++ b/rpcs3/util/vm_native.cpp @@ -86,14 +86,6 @@ namespace utils constexpr int c_mfd_huge_2mb = 0; #endif -#ifndef MEM_RESERVE_PLACEHOLDER -#define MEM_RESERVE_PLACEHOLDER 0x00040000 -#endif - -#ifndef MEM_REPLACE_PLACEHOLDER -#define MEM_REPLACE_PLACEHOLDER 0x00004000 -#endif - #ifdef _WIN32 DYNAMIC_IMPORT("KernelBase.dll", VirtualAlloc2, PVOID(HANDLE Process, PVOID Base, SIZE_T Size, ULONG AllocType, ULONG Prot, MEM_EXTENDED_PARAMETER*, ULONG)); DYNAMIC_IMPORT("KernelBase.dll", MapViewOfFile3, PVOID(HANDLE Handle, HANDLE Process, PVOID Base, ULONG64 Off, SIZE_T ViewSize, ULONG AllocType, ULONG Prot, MEM_EXTENDED_PARAMETER*, ULONG)); From a01a7a44cd120cd0f8d5d54bda2bda16fe9f2d7d Mon Sep 17 00:00:00 2001 From: oltolm Date: Fri, 28 Jul 2023 11:25:18 +0200 Subject: [PATCH 079/184] qt6: fix mingw-w64 build --- 3rdparty/llvm/CMakeLists.txt | 1 + 3rdparty/qt6.cmake | 3 +-- CMakeLists.txt | 2 -- rpcs3/CMakeLists.txt | 36 +----------------------------------- 4 files changed, 3 insertions(+), 39 deletions(-) diff --git a/3rdparty/llvm/CMakeLists.txt b/3rdparty/llvm/CMakeLists.txt index 1c9552e322..679cb23a61 100644 --- a/3rdparty/llvm/CMakeLists.txt +++ b/3rdparty/llvm/CMakeLists.txt @@ -15,6 +15,7 @@ if(WITH_LLVM) option(LLVM_INCLUDE_TOOLS OFF) option(LLVM_INCLUDE_UTILS OFF) option(LLVM_CCACHE_BUILD ON) + set(LLVM_ENABLE_WARNINGS OFF CACHE BOOL "Enable compiler warnings.") if(WIN32) set(LLVM_USE_INTEL_JITEVENTS ON) diff --git a/3rdparty/qt6.cmake b/3rdparty/qt6.cmake index 40d8e94df4..7fce290470 100644 --- a/3rdparty/qt6.cmake +++ b/3rdparty/qt6.cmake @@ -4,8 +4,7 @@ set(QT_MIN_VER 6.2.4) find_package(Qt6 ${QT_MIN_VER} CONFIG COMPONENTS Widgets Concurrent Multimedia MultimediaWidgets Svg SvgWidgets) if(WIN32) - find_package(Qt6 ${QT_MIN_VER} COMPONENTS WinExtras REQUIRED) - target_link_libraries(3rdparty_qt6 INTERFACE Qt6::Widgets Qt6::WinExtras Qt6::Concurrent Qt6::Multimedia Qt6::MultimediaWidgets Qt6::Svg Qt6::SvgWidgets) + target_link_libraries(3rdparty_qt6 INTERFACE Qt6::Widgets Qt6::Concurrent Qt6::Multimedia Qt6::MultimediaWidgets Qt6::Svg Qt6::SvgWidgets) else() find_package(Qt6 ${QT_MIN_VER} COMPONENTS DBus Gui) if(Qt6DBus_FOUND) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8887c21d36..e8b39077f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -115,8 +115,6 @@ if(APPLE AND CMAKE_OSX_ARCHITECTURES STREQUAL "arm64") link_directories(/opt/homebrew/lib) endif() -set(LLVM_ENABLE_WARNINGS OFF CACHE BOOL "Enable compiler warnings.") - if(MSVC) add_compile_options(/wd4530 /utf-8) # C++ exception handler used, but unwind semantics are not enabled endif() diff --git a/rpcs3/CMakeLists.txt b/rpcs3/CMakeLists.txt index f89c8b059a..0bf6f5ebbb 100644 --- a/rpcs3/CMakeLists.txt +++ b/rpcs3/CMakeLists.txt @@ -112,14 +112,6 @@ if(USE_PRECOMPILED_HEADERS) target_precompile_headers(rpcs3 PRIVATE stdafx.h) endif() -get_target_property(_qmake_executable Qt6::qmake IMPORTED_LOCATION) -get_filename_component(_qt_bin_dir "${_qmake_executable}" DIRECTORY) -if(APPLE) - find_program(MACDEPLOYQT_EXECUTABLE macdeployqt HINTS "${_qt_bin_dir}") -elseif(WIN32) - find_program(WINDEPLOYQT_EXECUTABLE windeployqt HINTS "${_qt_bin_dir}") -endif() - # Copy icons to executable directory if(APPLE) if (CMAKE_BUILD_TYPE MATCHES "Debug" OR CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo") @@ -148,35 +140,9 @@ elseif(UNIX) COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/git $/git) elseif(WIN32) - # TODO(cjj19970505@live.cn) - # offical Qt binaries are built with -MD(d) only as stated in offical wiki - # https://wiki.qt.io/Technical_FAQ#Why_does_a_statically_built_Qt_use_the_dynamic_Visual_Studio_runtime_libraries_.3F_Do_I_need_to_deploy_those_with_my_application_.3F - # If we build our libs with /MT(d), we might encounter some issues. - # https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=msvc-160#what-problems-exist-if-an-application-uses-more-than-one-crt-version - - # Qt installed from Qt installer has following hierarchy: - # bin/ for release and debug dlls and windeployqt tools - # lib/cmake/Qt6/ for Qt6_Dir - # Qt installed from vcpkg has following hierarchy: - # bin/ for release dlls - # debug/bin/ for debug dlls - # tools/qt6/bin/ for tools including windeployqt - # tools/qt6/debug/bin/ for tools with debug build including windeployqt - # share/cmake/Qt6/ for Qt6_Dir - - # If Qt is installed from official Qt installer - # list(APPEND _QT6_TOOLS_PATHS "${Qt6_DIR}/../../../bin/") - - # If Qt is installed from vcpkg - # list(APPEND _QT6_TOOLS_PATHS "${Qt6_DIR}/../../../tools/qt6$<$:/debug>/bin/") - add_custom_command(TARGET rpcs3 POST_BUILD - # COMMAND set PATH=${_QT6_TOOLS_PATHS}$%PATH% COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/bin" "$" - # If Qt is installed from vcpkg, add binary path to PATH - # otherwise windeployqt tool won't be able to locate necessary dlls - # COMMAND set PATH=${Qt6_DIR}/../../../$<$:debug/>bin/$%PATH% - COMMAND "${WINDEPLOYQT_EXECUTABLE}" --no-compiler-runtime --no-opengl-sw --no-patchqt --no-translations --no-quick --no-system-d3d-compiler --no-quick-import --plugindir "$/qt6/plugins" --verbose 0 "$") + COMMAND "${WINDEPLOYQT_EXECUTABLE}" --no-compiler-runtime --no-opengl-sw --no-patchqt --no-translations --no-quick --no-system-d3d-compiler --no-quick-import --plugindir "$,$/plugins,$/share/qt6/plugins>" --verbose 0 "$") endif() # Unix installation From d062fc1d3153de02e94cf211dcd974069207dc61 Mon Sep 17 00:00:00 2001 From: oltolm Date: Mon, 31 Jul 2023 21:27:39 +0200 Subject: [PATCH 080/184] sys_net.cpp: fix compiler warning --- rpcs3/Emu/Cell/lv2/sys_net.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/Emu/Cell/lv2/sys_net.cpp b/rpcs3/Emu/Cell/lv2/sys_net.cpp index 387cb3f623..026ab67559 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net.cpp @@ -257,7 +257,7 @@ lv2_socket::lv2_socket(utils::serial& ar, lv2_socket_type _type) ar.pos += 8; #endif - const s32 version = GET_SERIALIZATION_VERSION(lv2_net); + [[maybe_unused]] const s32 version = GET_SERIALIZATION_VERSION(lv2_net); ar(so_rcvtimeo, so_sendtimeo); From 01a05502e87b85527b069838cc698b388ab7e0ac Mon Sep 17 00:00:00 2001 From: oltolm Date: Wed, 2 Aug 2023 00:33:45 +0200 Subject: [PATCH 081/184] cmake: replace add_compile_definitions with target_compile_definitions --- CMakeLists.txt | 5 ----- rpcs3/CMakeLists.txt | 1 + rpcs3/Emu/CMakeLists.txt | 1 + rpcs3/rpcs3qt/CMakeLists.txt | 1 + 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e8b39077f2..4aa13efff4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,11 +105,6 @@ if(CCACHE_FOUND) set(CMAKE_CXX_COMPILER_LAUNCHER ccache) endif() -if(WIN32) - add_compile_definitions(UNICODE) - add_compile_definitions(_WIN32_WINNT=0x0602) -endif() - if(APPLE AND CMAKE_OSX_ARCHITECTURES STREQUAL "arm64") include_directories(/opt/homebrew/include) link_directories(/opt/homebrew/lib) diff --git a/rpcs3/CMakeLists.txt b/rpcs3/CMakeLists.txt index 0bf6f5ebbb..ebad9bfeb3 100644 --- a/rpcs3/CMakeLists.txt +++ b/rpcs3/CMakeLists.txt @@ -42,6 +42,7 @@ add_subdirectory(rpcs3qt) if(WIN32) add_executable(rpcs3 WIN32) target_sources(rpcs3 PRIVATE rpcs3.rc) + target_compile_definitions(rpcs3 PRIVATE UNICODE _UNICODE) elseif(APPLE) add_executable(rpcs3 MACOSX_BUNDLE) target_sources(rpcs3 PRIVATE rpcs3.icns) diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index 4c3e348ccc..b1673a8077 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -163,6 +163,7 @@ if(WIN32) Audio/XAudio2/XAudio2Backend.cpp Audio/XAudio2/xaudio2_enumerator.cpp ) + target_compile_definitions(rpcs3_emu PRIVATE UNICODE _UNICODE _WIN32_WINNT=0x0602) endif() target_link_libraries(rpcs3_emu diff --git a/rpcs3/rpcs3qt/CMakeLists.txt b/rpcs3/rpcs3qt/CMakeLists.txt index 45abc35bf8..7408976176 100644 --- a/rpcs3/rpcs3qt/CMakeLists.txt +++ b/rpcs3/rpcs3qt/CMakeLists.txt @@ -124,6 +124,7 @@ add_library(rpcs3_ui STATIC if(WIN32) target_sources(rpcs3_ui PUBLIC "../windows.qrc") + target_compile_definitions(rpcs3_ui PRIVATE UNICODE _UNICODE) endif() set_target_properties(rpcs3_ui From 384c807d6a29bfc3083463fb218816d8379ef48c Mon Sep 17 00:00:00 2001 From: Ivan Chikish Date: Wed, 16 Aug 2023 22:49:02 +0300 Subject: [PATCH 082/184] Fixup atomic wait (Linux) --- rpcs3/util/atomic.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rpcs3/util/atomic.cpp b/rpcs3/util/atomic.cpp index 97f0ac35d0..34b726c3b1 100644 --- a/rpcs3/util/atomic.cpp +++ b/rpcs3/util/atomic.cpp @@ -914,6 +914,8 @@ atomic_wait_engine::wait(const void* data, u32 old_value, u64 timeout, atomic_wa } return; } + + ext_size = 0; #endif if (!s_tls_wait_cb(data, 0, 0)) From dddd12f66baf983f20c1148861e460c2e8b69bfb Mon Sep 17 00:00:00 2001 From: Eladash Date: Wed, 16 Aug 2023 09:47:45 +0300 Subject: [PATCH 083/184] CELL: Postponed address notifications --- rpcs3/Emu/Cell/PPUThread.cpp | 31 +++++++++++++++++-- rpcs3/Emu/Cell/PPUThread.h | 1 + rpcs3/Emu/Cell/lv2/lv2.cpp | 57 +++++++++++++++++++++++++++++++++-- rpcs3/Emu/Cell/lv2/sys_sync.h | 3 +- 4 files changed, 86 insertions(+), 6 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 7ecb5d05b5..3df05e8421 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -3049,12 +3049,29 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) return false; }()) { - // Test a common pattern in lwmutex extern atomic_t liblv2_begin, liblv2_end; + const u32 notify = ppu.res_notify; + + if (notify) + { + vm::reservation_notifier(notify).notify_all(); + ppu.res_notify = 0; + } + + // Avoid notifications from lwmutex or sys_spinlock if (ppu.cia < liblv2_begin || ppu.cia >= liblv2_end) { - res.notify_all(); + if (!notify) + { + // Try to postpone notification to when PPU is asleep or join notifications on the same address + // This also optimizes a mutex - won't notify after lock is aqcuired (prolonging the critical section duration), only notifies on unlock + ppu.res_notify = addr; + } + else if ((addr ^ notify) & -128) + { + res.notify_all(); + } } if (addr == ppu.last_faddr) @@ -3066,6 +3083,16 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) return true; } + const u32 notify = ppu.res_notify; + + // Do not risk postponing too much (because this is probably an indefinite loop) + // And on failure it has some time to do something else + if (notify && ((addr ^ notify) & -128)) + { + vm::reservation_notifier(notify).notify_all(); + ppu.res_notify = 0; + } + return false; } diff --git a/rpcs3/Emu/Cell/PPUThread.h b/rpcs3/Emu/Cell/PPUThread.h index 2bb03c412c..72b99eae61 100644 --- a/rpcs3/Emu/Cell/PPUThread.h +++ b/rpcs3/Emu/Cell/PPUThread.h @@ -262,6 +262,7 @@ public: u64 rtime{0}; alignas(64) std::byte rdata[128]{}; // Reservation data bool use_full_rdata{}; + u32 res_notify{}; union ppu_prio_t { diff --git a/rpcs3/Emu/Cell/lv2/lv2.cpp b/rpcs3/Emu/Cell/lv2/lv2.cpp index b265c83dfe..f49e070d03 100644 --- a/rpcs3/Emu/Cell/lv2/lv2.cpp +++ b/rpcs3/Emu/Cell/lv2/lv2.cpp @@ -2,6 +2,7 @@ #include "Emu/System.h" #include "Emu/system_config.h" #include "Emu/Memory/vm_ptr.h" +#include "Emu/Memory/vm_reservation.h" #include "Emu/Memory/vm_locking.h" #include "Emu/Cell/PPUFunction.h" @@ -1268,6 +1269,31 @@ bool lv2_obj::sleep(cpu_thread& cpu, const u64 timeout) prepare_for_sleep(cpu); } + if (cpu.id_type() == 1) + { + if (u32 addr = static_cast(cpu).res_notify) + { + static_cast(cpu).res_notify = 0; + + const usz notify_later_idx = std::basic_string_view{g_to_notify, std::size(g_to_notify)}.find_first_of(std::add_pointer_t{}); + + if (notify_later_idx != umax) + { + g_to_notify[notify_later_idx] = &vm::reservation_notifier(addr); + + if (notify_later_idx < std::size(g_to_notify) - 1) + { + // Null-terminate the list if it ends before last slot + g_to_notify[notify_later_idx + 1] = nullptr; + } + } + else + { + vm::reservation_notifier(addr).notify_all(); + } + } + } + bool result = false; const u64 current_time = get_guest_system_time(); { @@ -1294,6 +1320,31 @@ bool lv2_obj::sleep(cpu_thread& cpu, const u64 timeout) bool lv2_obj::awake(cpu_thread* thread, s32 prio) { + if (ppu_thread* ppu = cpu_thread::get_current()) + { + if (u32 addr = ppu->res_notify) + { + ppu->res_notify = 0; + + const usz notify_later_idx = std::basic_string_view{g_to_notify, std::size(g_to_notify)}.find_first_of(std::add_pointer_t{}); + + if (notify_later_idx != umax) + { + g_to_notify[notify_later_idx] = &vm::reservation_notifier(addr); + + if (notify_later_idx < std::size(g_to_notify) - 1) + { + // Null-terminate the list if it ends before last slot + g_to_notify[notify_later_idx + 1] = nullptr; + } + } + else + { + vm::reservation_notifier(addr).notify_all(); + } + } + } + bool result = false; { std::lock_guard lock(g_mutex); @@ -1673,7 +1724,7 @@ void lv2_obj::cleanup() void lv2_obj::schedule_all(u64 current_time) { - usz notify_later_idx = 0; + usz notify_later_idx = std::basic_string_view{g_to_notify, std::size(g_to_notify)}.find_first_of(std::add_pointer_t{}); if (!g_pending && g_scheduler_ready) { @@ -1692,7 +1743,7 @@ void lv2_obj::schedule_all(u64 current_time) continue; } - if (notify_later_idx == std::size(g_to_notify)) + if (notify_later_idx >= std::size(g_to_notify)) { // Out of notification slots, notify locally (resizable container is not worth it) target->state.notify_one(); @@ -1726,7 +1777,7 @@ void lv2_obj::schedule_all(u64 current_time) ensure(!target->state.test_and_set(cpu_flag::notify)); // Otherwise notify it to wake itself - if (notify_later_idx == std::size(g_to_notify)) + if (notify_later_idx >= std::size(g_to_notify)) { // Out of notification slots, notify locally (resizable container is not worth it) target->state.notify_one(); diff --git a/rpcs3/Emu/Cell/lv2/sys_sync.h b/rpcs3/Emu/Cell/lv2/sys_sync.h index 28a04d281a..3233213613 100644 --- a/rpcs3/Emu/Cell/lv2/sys_sync.h +++ b/rpcs3/Emu/Cell/lv2/sys_sync.h @@ -465,7 +465,8 @@ public: // While IDM mutex is still locked (this function assumes so) check if the notification is still needed // Pending flag is meant for forced notification (if the CPU really has pending work it can restore the flag in theory) - if (cpu != &g_to_notify && static_cast(cpu)->none_of(cpu_flag::signal + cpu_flag::pending)) + // Disabled to allow reservation notifications from here + if (false && cpu != &g_to_notify && static_cast(cpu)->none_of(cpu_flag::signal + cpu_flag::pending)) { // Omit it (this is a void pointer, it can hold anything) cpu = &g_to_notify; From 6adc7f9ee65177e6f99ebfcde9044088e2a28076 Mon Sep 17 00:00:00 2001 From: Eladash Date: Wed, 16 Aug 2023 16:16:49 +0300 Subject: [PATCH 084/184] SPU: Use usermode waiting for busy GETLLAR loop --- rpcs3/Emu/Cell/SPUThread.cpp | 70 +++++++++++++++++++++++++++++++++++- rpcs3/util/sysinfo.cpp | 7 ++++ rpcs3/util/sysinfo.hpp | 2 ++ 3 files changed, 78 insertions(+), 1 deletion(-) diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 4fdd72f143..64747776b7 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -37,6 +37,15 @@ #include "util/sysinfo.hpp" #include "util/serialization.hpp" +#if defined(ARCH_X64) +#ifdef _MSC_VER +#include +#include +#else +#include +#endif +#endif + using spu_rdata_t = decltype(spu_thread::rdata); template <> @@ -320,6 +329,40 @@ extern void mov_rdata_nt(spu_rdata_t& _dst, const spu_rdata_t& _src) #endif } +#if defined(_MSC_VER) +#define mwaitx_func +#define waitpkg_func +#else +#define mwaitx_func __attribute__((__target__("mwaitx"))) +#define waitpkg_func __attribute__((__target__("waitpkg"))) +#endif + +#if defined(ARCH_X64) +// Waits for a number of TSC clock cycles in power optimized state +// Cstate is represented in bits [7:4]+1 cstate. So C0 requires bits [7:4] to be set to 0xf, C1 requires bits [7:4] to be set to 0. +template +mwaitx_func static void __mwaitx(u32 cycles, u32 cstate, const void* cline, const Args&... args) +{ + constexpr u32 timer_enable = 0x2; + + // monitorx will wake if the cache line is written to, use it for reservations which fits it almost perfectly + _mm_monitorx(const_cast(cline), 0, 0); + + // Use static function to force inline + if (T::needs_wait(args...)) + { + _mm_mwaitx(timer_enable, cstate, cycles); + } +} + +// First bit indicates cstate, 0x0 for C.02 state (lower power) or 0x1 for C.01 state (higher power) +waitpkg_func static void __tpause(u32 cycles, u32 cstate) +{ + const u64 tsc = utils::get_tsc() + cycles; + _tpause(cstate, tsc); +} +#endif + void do_cell_atomic_128_store(u32 addr, const void* to_write); extern thread_local u64 g_tls_fault_spu; @@ -4113,7 +4156,32 @@ bool spu_thread::process_mfc_cmd() if (getllar_busy_waiting_switch == 1) { - busy_wait(300); +#if defined(ARCH_X64) + if (utils::has_um_wait()) + { + if (utils::has_waitpkg()) + { + __tpause(std::min(getllar_spin_count, 10) * 500, 0x1); + } + else + { + struct check_wait_t + { + static FORCE_INLINE bool needs_wait(u64 rtime, const atomic_t& mem_rtime) noexcept + { + return rtime == mem_rtime; + } + }; + + // Provide the first X64 cache line of the reservation to be tracked + __mwaitx(std::min(getllar_spin_count, 17) * 500, 0xf0, std::addressof(data), +rtime, vm::reservation_acquire(addr)); + } + } + else +#endif + { + busy_wait(300); + } } return true; diff --git a/rpcs3/util/sysinfo.cpp b/rpcs3/util/sysinfo.cpp index 24a264a32f..108450ab30 100755 --- a/rpcs3/util/sysinfo.cpp +++ b/rpcs3/util/sysinfo.cpp @@ -358,6 +358,13 @@ bool utils::has_appropriate_um_wait() #endif } +// Similar to the above function but allow execution if alternatives such as yield are not wanted +bool utils::has_um_wait() +{ + static const bool g_value = (has_waitx() || has_waitpkg()) && get_tsc_freq(); + return g_value; +} + u32 utils::get_rep_movsb_threshold() { static const u32 g_value = []() diff --git a/rpcs3/util/sysinfo.hpp b/rpcs3/util/sysinfo.hpp index 2ce46b7c6c..d7c7c2ec0e 100755 --- a/rpcs3/util/sysinfo.hpp +++ b/rpcs3/util/sysinfo.hpp @@ -59,6 +59,8 @@ namespace utils bool has_appropriate_um_wait(); + bool has_um_wait(); + std::string get_cpu_brand(); std::string get_system_info(); From dacb0bd87fdb043d3d2ee409d5ed262a28fb5609 Mon Sep 17 00:00:00 2001 From: Eladash Date: Thu, 17 Aug 2023 11:02:40 +0300 Subject: [PATCH 085/184] utils/endian.hpp: Use std::byteswap --- rpcs3/util/endian.hpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/rpcs3/util/endian.hpp b/rpcs3/util/endian.hpp index b9d1b19d0c..c001419ea1 100644 --- a/rpcs3/util/endian.hpp +++ b/rpcs3/util/endian.hpp @@ -35,7 +35,9 @@ namespace stx static constexpr u16 swap(u16 src) noexcept { -#if defined(__GNUG__) +#if __cpp_lib_byteswap >= 202110L + return std::byteswap(src); +#elif defined(__GNUG__) return __builtin_bswap16(src); #else if (std::is_constant_evaluated()) @@ -55,7 +57,9 @@ namespace stx static constexpr u32 swap(u32 src) noexcept { -#if defined(__GNUG__) +#if __cpp_lib_byteswap >= 202110L + return std::byteswap(src); +#elif defined(__GNUG__) return __builtin_bswap32(src); #else if (std::is_constant_evaluated()) @@ -76,7 +80,9 @@ namespace stx static constexpr u64 swap(u64 src) noexcept { -#if defined(__GNUG__) +#if __cpp_lib_byteswap >= 202110L + return std::byteswap(src); +#elif defined(__GNUG__) return __builtin_bswap64(src); #else if (std::is_constant_evaluated()) From c8f8ecc4d69bd0a8ce98762d07149d07de06c645 Mon Sep 17 00:00:00 2001 From: Eladash Date: Thu, 17 Aug 2023 12:35:20 +0300 Subject: [PATCH 086/184] LV2: Fixup reservation notifications --- rpcs3/Emu/Cell/lv2/sys_sync.h | 17 ++++++++++++++--- rpcs3/Emu/Memory/vm.h | 2 +- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/rpcs3/Emu/Cell/lv2/sys_sync.h b/rpcs3/Emu/Cell/lv2/sys_sync.h index 3233213613..bd09d26a61 100644 --- a/rpcs3/Emu/Cell/lv2/sys_sync.h +++ b/rpcs3/Emu/Cell/lv2/sys_sync.h @@ -60,6 +60,11 @@ enum enum ppu_thread_status : u32; +namespace vm +{ + extern u8 g_reservations[65536 / 128 * 64]; +} + // Base class for some kernel objects (shared set of 8192 objects). struct lv2_obj { @@ -432,9 +437,15 @@ public: if (cpu != &g_to_notify) { - // Note: by the time of notification the thread could have been deallocated which is why the direct function is used - // TODO: Pass a narrower mask - atomic_wait_engine::notify_one(cpu); + if (cpu >= vm::g_reservations && cpu <= vm::g_reservations + (std::size(vm::g_reservations) - 1)) + { + atomic_wait_engine::notify_all(cpu); + } + else + { + // Note: by the time of notification the thread could have been deallocated which is why the direct function is used + atomic_wait_engine::notify_one(cpu); + } } } diff --git a/rpcs3/Emu/Memory/vm.h b/rpcs3/Emu/Memory/vm.h index 34e3bbe032..f008c9aee9 100644 --- a/rpcs3/Emu/Memory/vm.h +++ b/rpcs3/Emu/Memory/vm.h @@ -22,7 +22,7 @@ namespace vm extern u8* const g_exec_addr; extern u8* const g_stat_addr; extern u8* const g_free_addr; - extern u8 g_reservations[]; + extern u8 g_reservations[65536 / 128 * 64]; struct writer_lock; From 8b212f216992016cf875b0e49918994edc353f33 Mon Sep 17 00:00:00 2001 From: Eladash Date: Thu, 17 Aug 2023 16:26:46 +0300 Subject: [PATCH 087/184] PPU: Fix LVRX bad memory access --- rpcs3/Emu/Cell/PPUInterpreter.cpp | 4 +++- rpcs3/Emu/Cell/PPUTranslator.cpp | 7 +++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUInterpreter.cpp b/rpcs3/Emu/Cell/PPUInterpreter.cpp index 53e18db82b..f5d06ed57a 100644 --- a/rpcs3/Emu/Cell/PPUInterpreter.cpp +++ b/rpcs3/Emu/Cell/PPUInterpreter.cpp @@ -5204,7 +5204,9 @@ auto LVRX() static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) { const u64 addr = op.ra ? ppu.gpr[op.ra] + ppu.gpr[op.rb] : ppu.gpr[op.rb]; - const u128 data = ppu_feed_data(ppu, addr & -16); + + // Read from instruction address if offset is 0, this prevents accessing potentially bad memory from addr (because no actual memory is dereferenced) + const u128 data = ppu_feed_data(ppu, ((addr & 15) == 0 ? ppu.cia : addr) & -16); ppu.vr[op.vd] = data >> ((~addr & 15) * 8) >> 8; }; diff --git a/rpcs3/Emu/Cell/PPUTranslator.cpp b/rpcs3/Emu/Cell/PPUTranslator.cpp index e591a5fada..8b3ac0b402 100644 --- a/rpcs3/Emu/Cell/PPUTranslator.cpp +++ b/rpcs3/Emu/Cell/PPUTranslator.cpp @@ -3310,8 +3310,11 @@ void PPUTranslator::SRD(ppu_opcode_t op) void PPUTranslator::LVRX(ppu_opcode_t op) { const auto addr = op.ra ? m_ir->CreateAdd(GetGpr(op.ra), GetGpr(op.rb)) : GetGpr(op.rb); - const auto data = ReadMemory(m_ir->CreateAnd(addr, ~0xfull), GetType(), m_is_be, 16); - set_vr(op.vd, pshufb(value(data), build(255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241, 240) + vsplat(trunc(value(addr) & 0xf)))); + const auto offset = eval(trunc(value(addr) & 0xf)); + + // Read from instruction address if offset is 0, this prevents accessing potentially bad memory from addr (because no actual memory is dereferenced) + const auto data = ReadMemory(m_ir->CreateAnd(m_ir->CreateSelect(m_ir->CreateIsNull(offset.value), m_reloc ? m_seg0 : GetAddr(0), addr), ~0xfull), GetType(), m_is_be, 16); + set_vr(op.vd, pshufb(value(data), build(255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241, 240) + vsplat(offset))); } void PPUTranslator::LSWI(ppu_opcode_t op) From e410841f10c488fbe1d9836acad0bbfd897dd1d1 Mon Sep 17 00:00:00 2001 From: Zion Nimchuk Date: Tue, 15 Aug 2023 09:56:28 -0700 Subject: [PATCH 088/184] Update focal --- .cirrus.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index 437c7dbe90..144feb8efd 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -61,7 +61,7 @@ windows_task: linux_task: container: - image: rpcs3/rpcs3-ci-focal:1.0 + image: rpcs3/rpcs3-ci-focal:1.1 cpu: 4 memory: 16G env: From 730badd378f636ca3478b245bfdbc527b261c1a0 Mon Sep 17 00:00:00 2001 From: Eladash Date: Fri, 18 Aug 2023 08:54:57 +0300 Subject: [PATCH 089/184] cellAudio: Move and partially fix _mxr000 hack --- rpcs3/Emu/Cell/Modules/cellAudio.cpp | 103 +++++++++++++++++++++++--- rpcs3/Emu/Cell/Modules/cellGame.cpp | 2 +- rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp | 26 ------- 3 files changed, 94 insertions(+), 37 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellAudio.cpp b/rpcs3/Emu/Cell/Modules/cellAudio.cpp index dfc3d6ea24..1dcbb6ceae 100644 --- a/rpcs3/Emu/Cell/Modules/cellAudio.cpp +++ b/rpcs3/Emu/Cell/Modules/cellAudio.cpp @@ -10,6 +10,8 @@ LOG_CHANNEL(cellAudio); +extern void lv2_sleep(u64 timeout, ppu_thread* ppu = nullptr); + vm::gvar g_audio_buffer; struct alignas(16) aligned_index_t @@ -1252,8 +1254,6 @@ error_code cellAudioPortOpen(vm::ptr audioParam, vm::ptrget(); - std::lock_guard lock(g_audio.mutex); - if (!g_audio.init) { return CELL_AUDIO_ERROR_NOT_INIT; @@ -1319,6 +1319,16 @@ error_code cellAudioPortOpen(vm::ptr audioParam, vm::ptrget(); - std::lock_guard lock(g_audio.mutex); - if (!g_audio.init) { return CELL_AUDIO_ERROR_NOT_INIT; @@ -1422,6 +1430,16 @@ error_code cellAudioPortStart(u32 portNum) return CELL_AUDIO_ERROR_PARAM; } + // Waiting for VSH + lv2_sleep(30); + + std::lock_guard lock(g_audio.mutex); + + if (!g_audio.init) + { + return CELL_AUDIO_ERROR_NOT_INIT; + } + switch (audio_port_state state = g_audio.ports[portNum].state.compare_and_swap(audio_port_state::opened, audio_port_state::started)) { case audio_port_state::closed: return CELL_AUDIO_ERROR_PORT_NOT_OPEN; @@ -1650,10 +1668,69 @@ error_code cellAudioCreateNotifyEventQueueEx(ppu_thread& ppu, vm::ptr id, v return AudioCreateNotifyEventQueue(ppu, id, key, queue_type); } -error_code AudioSetNotifyEventQueue(u64 key, u32 iFlags) +error_code AudioSetNotifyEventQueue(ppu_thread& ppu, u64 key, u32 iFlags) { auto& g_audio = g_fxo->get(); + if (!g_audio.init) + { + return CELL_AUDIO_ERROR_NOT_INIT; + } + + // Waiting for VSH + lv2_sleep(20, &ppu); + + // Dirty hack for sound: confirm the creation of _mxr000 event queue by _cellsurMixerMain thread + constexpr u64 c_mxr000 = 0x8000cafe0246030; + + if (key == c_mxr000 || key == 0) + { + bool has_sur_mixer_thread = false; + + for (usz count = 0; !lv2_event_queue::find(c_mxr000) && count < 100; count++) + { + if (has_sur_mixer_thread || idm::select>([&](u32 id, named_thread& test_ppu) + { + // Confirm thread existence + if (id == ppu.id) + { + return false; + } + + const auto ptr = test_ppu.ppu_tname.load(); + + if (!ptr) + { + return false; + } + + return *ptr == "_cellsurMixerMain"sv; + }).ret) + { + has_sur_mixer_thread = true; + } + else + { + break; + } + + if (ppu.is_stopped()) + { + ppu.state += cpu_flag::again; + return {}; + } + + cellAudio.error("AudioSetNotifyEventQueue(): Waiting for _mxr000. x%d", count); + + lv2_sleep(50'000, &ppu); + } + + if (has_sur_mixer_thread && lv2_event_queue::find(c_mxr000)) + { + key = c_mxr000; + } + } + std::lock_guard lock(g_audio.mutex); if (!g_audio.init) @@ -1687,27 +1764,33 @@ error_code AudioSetNotifyEventQueue(u64 key, u32 iFlags) } // Set unique source associated with the key - g_audio.keys.push_back({ + g_audio.keys.push_back + ({ .start_period = g_audio.event_period, .flags = iFlags, .source = ((process_getpid() + u64{}) << 32) + lv2_event_port::id_base + (g_audio.key_count++ * lv2_event_port::id_step), .ack_timestamp = 0, .port = std::move(q) }); + g_audio.key_count %= lv2_event_port::id_count; return CELL_OK; } -error_code cellAudioSetNotifyEventQueue(u64 key) +error_code cellAudioSetNotifyEventQueue(ppu_thread& ppu, u64 key) { + ppu.state += cpu_flag::wait; + cellAudio.warning("cellAudioSetNotifyEventQueue(key=0x%llx)", key); - return AudioSetNotifyEventQueue(key, 0); + return AudioSetNotifyEventQueue(ppu, key, 0); } -error_code cellAudioSetNotifyEventQueueEx(u64 key, u32 iFlags) +error_code cellAudioSetNotifyEventQueueEx(ppu_thread& ppu, u64 key, u32 iFlags) { + ppu.state += cpu_flag::wait; + cellAudio.todo("cellAudioSetNotifyEventQueueEx(key=0x%llx, iFlags=0x%x)", key, iFlags); if (iFlags & (~0u >> 5)) @@ -1715,7 +1798,7 @@ error_code cellAudioSetNotifyEventQueueEx(u64 key, u32 iFlags) return CELL_AUDIO_ERROR_PARAM; } - return AudioSetNotifyEventQueue(key, iFlags); + return AudioSetNotifyEventQueue(ppu, key, iFlags); } error_code AudioRemoveNotifyEventQueue(u64 key, u32 iFlags) diff --git a/rpcs3/Emu/Cell/Modules/cellGame.cpp b/rpcs3/Emu/Cell/Modules/cellGame.cpp index 61f9dbc95a..27766431d3 100644 --- a/rpcs3/Emu/Cell/Modules/cellGame.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGame.cpp @@ -349,7 +349,7 @@ void disc_change_manager::insert_disc(u32 disc_type, std::string title_id) }); } -void lv2_sleep(u64 timeout, ppu_thread* ppu = nullptr) +extern void lv2_sleep(u64 timeout, ppu_thread* ppu = nullptr) { if (!ppu) { diff --git a/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp b/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp index 1440a32883..e933549c65 100644 --- a/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp @@ -591,32 +591,6 @@ error_code sys_ppu_thread_start(ppu_thread& ppu, u32 thread_id) { thread->cmd_notify++; thread->cmd_notify.notify_one(); - - // Dirty hack for sound: confirm the creation of _mxr000 event queue - if (*thread->ppu_tname.load() == "_cellsurMixerMain"sv) - { - ppu.check_state(); - lv2_obj::sleep(ppu); - - while (!idm::select([](u32, lv2_event_queue& eq) - { - //some games do not set event queue name, though key seems constant for them - return (eq.name == "_mxr000\0"_u64) || (eq.key == 0x8000cafe02460300); - })) - { - if (ppu.is_stopped()) - { - return {}; - } - - thread_ctrl::wait_for(50000); - } - - if (ppu.test_stopped()) - { - return 0; - } - } } return CELL_OK; From bc09af4ad355130b6ce7ac616ee72fe4e7766273 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 18 Aug 2023 17:29:21 +0200 Subject: [PATCH 090/184] Update submodules Update rtmidi to 6.0.0 Update zlib to 1.3 Update SDL to 2.28.2 --- 3rdparty/libsdl-org/SDL | 2 +- 3rdparty/rtmidi/rtmidi | 2 +- 3rdparty/zlib/zlib | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/3rdparty/libsdl-org/SDL b/3rdparty/libsdl-org/SDL index 4761467b2e..031912c4b6 160000 --- a/3rdparty/libsdl-org/SDL +++ b/3rdparty/libsdl-org/SDL @@ -1 +1 @@ -Subproject commit 4761467b2e8cc7db3d6bc98747daca0051858f09 +Subproject commit 031912c4b6c5db80b443f04aa56fec3e4e645153 diff --git a/3rdparty/rtmidi/rtmidi b/3rdparty/rtmidi/rtmidi index 84a99422a3..1e5b49925a 160000 --- a/3rdparty/rtmidi/rtmidi +++ b/3rdparty/rtmidi/rtmidi @@ -1 +1 @@ -Subproject commit 84a99422a3faf1ab417fe71c0903a48debb9376a +Subproject commit 1e5b49925aa60065db52de44c366d446a902547b diff --git a/3rdparty/zlib/zlib b/3rdparty/zlib/zlib index 04f42ceca4..09155eaa2f 160000 --- a/3rdparty/zlib/zlib +++ b/3rdparty/zlib/zlib @@ -1 +1 @@ -Subproject commit 04f42ceca40f73e2978b50e93806c2a18c1281fc +Subproject commit 09155eaa2f9270dc4ed1fa13e2b4b2613e6e4851 From e8b9d208655883760d2db767823124b74502cf00 Mon Sep 17 00:00:00 2001 From: shinra-electric <50119606+shinra-electric@users.noreply.github.com> Date: Wed, 16 Aug 2023 22:10:28 +0200 Subject: [PATCH 091/184] Bump MoltenVK to 1.2.5 --- 3rdparty/MoltenVK/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/MoltenVK/CMakeLists.txt b/3rdparty/MoltenVK/CMakeLists.txt index ec0a9e2edf..a673db6e11 100644 --- a/3rdparty/MoltenVK/CMakeLists.txt +++ b/3rdparty/MoltenVK/CMakeLists.txt @@ -4,7 +4,7 @@ include(ExternalProject) ExternalProject_Add(moltenvk GIT_REPOSITORY https://github.com/KhronosGroup/MoltenVK.git - GIT_TAG 4c6bfbe + GIT_TAG b3c9f86 BUILD_IN_SOURCE 1 SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK CONFIGURE_COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/fetchDependencies" --macos From 454cb69700975e92d3ce18ca7c0365e4d957935c Mon Sep 17 00:00:00 2001 From: shinra-electric <50119606+shinra-electric@users.noreply.github.com> Date: Thu, 17 Aug 2023 11:25:50 +0200 Subject: [PATCH 092/184] Add link for homebrew MVK 1.2.5 to Mac build script --- .ci/build-mac.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.ci/build-mac.sh b/.ci/build-mac.sh index 0a94540537..a1467aca59 100755 --- a/.ci/build-mac.sh +++ b/.ci/build-mac.sh @@ -9,8 +9,8 @@ arch -x86_64 /usr/local/bin/brew update arch -x86_64 /usr/local/bin/brew install -f --overwrite llvm@16 glew cmake sdl2 vulkan-headers ffmpeg arch -x86_64 /usr/local/bin/brew link -f llvm@16 -# moltenvk based on commit for 1.2.4 release -wget https://raw.githubusercontent.com/Homebrew/homebrew-core/b233d4f9f40f26d81da11140defbfd578cfe4a69/Formula/molten-vk.rb +# moltenvk based on commit for 1.2.5 release +wget https://raw.githubusercontent.com/Homebrew/homebrew-core/055ae78415b61ecf1fa3de32b76b8a149855f903/Formula/m/molten-vk.rb arch -x86_64 /usr/local/bin/brew install -f --overwrite ./molten-vk.rb #export MACOSX_DEPLOYMENT_TARGET=12.0 export CXX=clang++ From cd98e84ca5e3fc7d3c258b3f7ef54aa177829047 Mon Sep 17 00:00:00 2001 From: Eladash Date: Sat, 24 Jun 2023 16:56:49 +0300 Subject: [PATCH 093/184] Debugger/RSX: Add FP/VP hash of current shader --- Utilities/Thread.cpp | 5 +++++ rpcs3/Emu/RSX/RSXThread.cpp | 19 +++++++++++++++++++ rpcs3/Emu/RSX/RSXThread.h | 1 + 3 files changed, 25 insertions(+) diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp index 0354da63a8..f8b13425c6 100644 --- a/Utilities/Thread.cpp +++ b/Utilities/Thread.cpp @@ -1661,6 +1661,11 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe } } + if (cpu) + { + cpu->state += cpu_flag::wait; + } + Emu.Pause(true); if (!g_tls_access_violation_recovered) diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index c5f2e90161..d480b93387 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -3089,6 +3089,25 @@ namespace rsx recovered_fifo_cmds_history.push({fifo_ctrl->last_cmd(), current_time}); } + std::string thread::dump_misc() const + { + std::string ret = cpu_thread::dump_misc(); + + const auto flags = +state; + + if (is_paused(flags) && flags & cpu_flag::wait) + { + fmt::append(ret, "\nFragment Program Hash: %X.fp", current_fragment_program.get_data() ? program_hash_util::fragment_program_utils::get_fragment_program_ucode_hash(current_fragment_program) : 0); + fmt::append(ret, "\nVertex Program Hash: %X.vp", current_vertex_program.data.empty() ? 0 : program_hash_util::vertex_program_utils::get_vertex_program_ucode_hash(current_vertex_program)); + } + else + { + fmt::append(ret, "\n"); + } + + return ret; + } + std::vector> thread::dump_callstack_list() const { std::vector> result; diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index ae91c502a0..18306cc4cf 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -173,6 +173,7 @@ namespace rsx std::unique_ptr fifo_ctrl; atomic_t rsx_thread_running{ false }; std::vector> dump_callstack_list() const override; + std::string dump_misc() const override; protected: FIFO::flattening_helper m_flattener; From f1d9e894184766e98365b855a2e34a626ab63755 Mon Sep 17 00:00:00 2001 From: Eladash Date: Fri, 18 Aug 2023 17:06:22 +0300 Subject: [PATCH 094/184] Cg Disasm: Fix instruction highlighting patter Highlighted wrongly the "Loading..." because L is an uppercase letter. --- rpcs3/rpcs3qt/syntax_highlighter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/rpcs3qt/syntax_highlighter.cpp b/rpcs3/rpcs3qt/syntax_highlighter.cpp index da33f40c60..4d04e66e2b 100644 --- a/rpcs3/rpcs3qt/syntax_highlighter.cpp +++ b/rpcs3/rpcs3qt/syntax_highlighter.cpp @@ -67,7 +67,7 @@ LogHighlighter::LogHighlighter(QTextDocument* parent) : Highlighter(parent) AsmHighlighter::AsmHighlighter(QTextDocument *parent) : Highlighter(parent) { - addRule("^[A-Z0-9]+", Qt::darkBlue); // Instructions + addRule("^\b[A-Z0-9]+\b", Qt::darkBlue); // Instructions addRule("-?R\\d[^,;\\s]*", Qt::darkRed); // -R0.* addRule("-?H\\d[^,;\\s]*", Qt::red); // -H1.* addRule("-?v\\[\\d\\]*[^,;\\s]*", Qt::darkCyan); // -v[xyz].* From 373e50250139f4cc6fb7496a3d53c583f8cb7ea6 Mon Sep 17 00:00:00 2001 From: oltolm Date: Sat, 19 Aug 2023 12:30:46 +0200 Subject: [PATCH 095/184] Workaround for Clang: move bless to its own header file --- Utilities/lockless.h | 2 +- rpcs3/emucore.vcxproj | 1 + rpcs3/emucore.vcxproj.filters | 3 +++ rpcs3/util/asm.hpp | 17 ----------------- rpcs3/util/bless.hpp | 21 +++++++++++++++++++++ rpcs3/util/shared_ptr.hpp | 2 +- 6 files changed, 27 insertions(+), 19 deletions(-) create mode 100644 rpcs3/util/bless.hpp diff --git a/Utilities/lockless.h b/Utilities/lockless.h index 7bd0133346..87e726ee3c 100644 --- a/Utilities/lockless.h +++ b/Utilities/lockless.h @@ -2,7 +2,7 @@ #include "util/types.hpp" #include "util/atomic.hpp" -#include "util/asm.hpp" +#include "util/bless.hpp" //! Simple unshrinkable array base for concurrent access. Only growths automatically. //! There is no way to know the current size. The smaller index is, the faster it's accessed. diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 17051eb305..4aaaf7ef2d 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -611,6 +611,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 0adf76dedf..fb5bda5d41 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -2371,6 +2371,9 @@ Emu\GPU\RSX\Common + + Utilities +
diff --git a/rpcs3/util/asm.hpp b/rpcs3/util/asm.hpp index 6afc032390..da0be9f1f6 100644 --- a/rpcs3/util/asm.hpp +++ b/rpcs3/util/asm.hpp @@ -410,23 +410,6 @@ namespace utils return factor1 > 0 && T{umax} / factor1 < factor2 ? T{umax} : static_cast(factor1 * factor2); } - // Hack. Pointer cast util to workaround UB. Use with extreme care. - template - [[nodiscard]] T* bless(U* ptr) - { -#ifdef _MSC_VER - return (T*)ptr; -#elif defined(ARCH_X64) - T* result; - __asm__("movq %1, %0;" : "=r" (result) : "r" (ptr) : "memory"); - return result; -#elif defined(ARCH_ARM64) - T* result; - __asm__("mov %0, %1" : "=r" (result) : "r" (ptr) : "memory"); - return result; -#endif - } - inline void trap() { #ifdef _M_X64 diff --git a/rpcs3/util/bless.hpp b/rpcs3/util/bless.hpp new file mode 100644 index 0000000000..49ffa8facb --- /dev/null +++ b/rpcs3/util/bless.hpp @@ -0,0 +1,21 @@ +#pragma once + +namespace utils +{ + // Hack. Pointer cast util to workaround UB. Use with extreme care. + template + [[nodiscard]] T* bless(U* ptr) + { +#ifdef _MSC_VER + return (T*)ptr; +#elif defined(ARCH_X64) + T* result; + __asm__("movq %1, %0;" : "=r" (result) : "r" (ptr) : "memory"); + return result; +#elif defined(ARCH_ARM64) + T* result; + __asm__("mov %0, %1" : "=r" (result) : "r" (ptr) : "memory"); + return result; +#endif + } +} diff --git a/rpcs3/util/shared_ptr.hpp b/rpcs3/util/shared_ptr.hpp index 23d4bfd974..109e28216a 100644 --- a/rpcs3/util/shared_ptr.hpp +++ b/rpcs3/util/shared_ptr.hpp @@ -3,7 +3,7 @@ #include #include #include "atomic.hpp" -#include "asm.hpp" +#include "bless.hpp" namespace stx { From 9635417ae5814c669c8849b73ea293e90dd073db Mon Sep 17 00:00:00 2001 From: Eladash Date: Sat, 19 Aug 2023 12:20:06 +0300 Subject: [PATCH 096/184] Replace some utils::bless usages --- rpcs3/Emu/NP/rpcn_client.cpp | 18 +++++++++--------- rpcs3/Emu/NP/rpcn_client.h | 2 +- rpcs3/Emu/RSX/RSXFIFO.cpp | 2 +- rpcs3/rpcs3qt/rsx_debugger.cpp | 10 +++++----- rpcs3/rpcs3qt/skylander_dialog.cpp | 12 ++++++------ 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/rpcs3/Emu/NP/rpcn_client.cpp b/rpcs3/Emu/NP/rpcn_client.cpp index 41bc86c698..5d640a2261 100644 --- a/rpcs3/Emu/NP/rpcn_client.cpp +++ b/rpcs3/Emu/NP/rpcn_client.cpp @@ -292,8 +292,8 @@ namespace rpcn { if (msg.size() == 6) { - addr_sig = *utils::bless>(&msg[0]); - port_sig = *utils::bless>(&msg[4]); + addr_sig = read_from_ptr>(&msg[0]); + port_sig = read_from_ptr>(&msg[4]); last_pong_time = now; } @@ -308,8 +308,8 @@ namespace rpcn { std::vector ping(13); ping[0] = 1; - *utils::bless>(&ping[1]) = user_id; - *utils::bless>(&ping[9]) = local_addr_sig; + write_to_ptr>(ping, 1, user_id); + write_to_ptr>(ping, 9, +local_addr_sig); if (send_packet_from_p2p_port(ping, addr_rpcn_udp) == -1) { rpcn_log.error("Failed to send ping to RPCN!"); @@ -354,9 +354,9 @@ namespace rpcn } const u8 packet_type = header[0]; - const u16 command = *utils::bless>(&header[1]); - const u32 packet_size = *utils::bless>(&header[3]); - const u64 packet_id = *utils::bless>(&header[7]); + const u16 command = read_from_ptr>(&header[1]); + const u32 packet_size = read_from_ptr>(&header[3]); + const u64 packet_id = read_from_ptr>(&header[7]); if (packet_size < RPCN_HEADER_SIZE) return error_and_disconnect("Invalid packet size"); @@ -1707,7 +1707,7 @@ namespace rpcn std::vector data(COMMUNICATION_ID_SIZE + sizeof(u64)); memcpy(data.data(), communication_id.data, COMMUNICATION_ID_SIZE); - *utils::bless>(&data[COMMUNICATION_ID_SIZE]) = room_id; + write_to_ptr>(data, COMMUNICATION_ID_SIZE, room_id); return forge_send(CommandType::PingRoomOwner, req_id, data); } @@ -1811,7 +1811,7 @@ namespace rpcn { std::vector data(COMMUNICATION_ID_SIZE + sizeof(u32)); memcpy(data.data(), communication_id.data, COMMUNICATION_ID_SIZE); - *utils::bless>(&data[COMMUNICATION_ID_SIZE]) = board_id; + write_to_ptr>(data, COMMUNICATION_ID_SIZE, board_id); return forge_send(CommandType::GetBoardInfos, req_id, data); } diff --git a/rpcs3/Emu/NP/rpcn_client.h b/rpcs3/Emu/NP/rpcn_client.h index 046b27665f..7bbd908a4f 100644 --- a/rpcs3/Emu/NP/rpcn_client.h +++ b/rpcs3/Emu/NP/rpcn_client.h @@ -59,7 +59,7 @@ public: error = true; return 0; } - T res = *utils::bless>(&vec[i]); + T res = read_from_ptr>(&vec[i]); i += sizeof(T); return res; } diff --git a/rpcs3/Emu/RSX/RSXFIFO.cpp b/rpcs3/Emu/RSX/RSXFIFO.cpp index 8dcc7dbad9..c4e52c6e6b 100644 --- a/rpcs3/Emu/RSX/RSXFIFO.cpp +++ b/rpcs3/Emu/RSX/RSXFIFO.cpp @@ -195,7 +195,7 @@ namespace rsx } } - const auto ret = utils::bless>(&m_cache)[(addr - m_cache_addr) >> 2]; + const auto ret = read_from_ptr>(+m_cache[0], addr - m_cache_addr); return {true, ret}; } diff --git a/rpcs3/rpcs3qt/rsx_debugger.cpp b/rpcs3/rpcs3qt/rsx_debugger.cpp index 9074ad7977..ea06da2508 100644 --- a/rpcs3/rpcs3qt/rsx_debugger.cpp +++ b/rpcs3/rpcs3qt/rsx_debugger.cpp @@ -426,12 +426,12 @@ namespace { case rsx::surface_color_format::b8: { - const u8 value = utils::bless(orig_buffer)[idx]; + const u8 value = read_from_ptr(orig_buffer, idx); return{ value, value, value }; } case rsx::surface_color_format::x32: { - const be_t stored_val = utils::bless>(orig_buffer)[idx]; + const be_t stored_val = read_from_ptr>(orig_buffer, idx); const u32 swapped_val = stored_val; const f32 float_val = std::bit_cast(swapped_val); const u8 val = float_val * 255.f; @@ -441,14 +441,14 @@ namespace case rsx::surface_color_format::x8b8g8r8_o8b8g8r8: case rsx::surface_color_format::x8b8g8r8_z8b8g8r8: { - const auto ptr = utils::bless(orig_buffer); + const auto ptr = reinterpret_cast(orig_buffer.data()); return{ ptr[1 + idx * 4], ptr[2 + idx * 4], ptr[3 + idx * 4] }; } case rsx::surface_color_format::a8r8g8b8: case rsx::surface_color_format::x8r8g8b8_o8r8g8b8: case rsx::surface_color_format::x8r8g8b8_z8r8g8b8: { - const auto ptr = utils::bless(orig_buffer); + const auto ptr = reinterpret_cast(orig_buffer.data()); return{ ptr[3 + idx * 4], ptr[2 + idx * 4], ptr[1 + idx * 4] }; } case rsx::surface_color_format::w16z16y16x16: @@ -581,7 +581,7 @@ void rsx_debugger::OnClickDrawCalls() { for (u32 col = 0; col < width; col++) { - const u8 stencil_val = utils::bless(orig_buffer)[row * width + col]; + const u8 stencil_val = reinterpret_cast(orig_buffer.data())[row * width + col]; buffer[4 * col + 0 + width * row * 4] = stencil_val; buffer[4 * col + 1 + width * row * 4] = stencil_val; buffer[4 * col + 2 + width * row * 4] = stencil_val; diff --git a/rpcs3/rpcs3qt/skylander_dialog.cpp b/rpcs3/rpcs3qt/skylander_dialog.cpp index c994c549ab..68cc771207 100644 --- a/rpcs3/rpcs3qt/skylander_dialog.cpp +++ b/rpcs3/rpcs3qt/skylander_dialog.cpp @@ -643,17 +643,17 @@ skylander_creator_dialog::skylander_creator_dialog(QWidget* parent) std::array buf{}; const auto data = buf.data(); // Set the block permissions - *utils::bless>(&data[0x36]) = 0x690F0F0F; + write_to_ptr>(data, 0x36, 0x690F0F0F); for (u32 index = 1; index < 0x10; index++) { - *utils::bless>(&data[(index * 0x40) + 0x36]) = 0x69080F7F; + write_to_ptr>(data, (index * 0x40) + 0x36, 0x69080F7F); } // Set the skylander infos - *utils::bless>(&data[0]) = (sky_id | sky_var) + 1; - *utils::bless>(&data[0x10]) = sky_id; - *utils::bless>(&data[0x1C]) = sky_var; + write_to_ptr>(data, (sky_id | sky_var) + 1); + write_to_ptr>(data, 0x10, sky_id); + write_to_ptr>(data, 0x1C, sky_var); // Set checksum - *utils::bless>(&data[0x1E]) = skylander_crc16(0xFFFF, data, 0x1E); + write_to_ptr>(data, 0x1E, skylander_crc16(0xFFFF, data, 0x1E)); sky_file.write(buf.data(), buf.size()); sky_file.close(); From 8236a0fa2d2f32fc11793034717fbad0250340ae Mon Sep 17 00:00:00 2001 From: Eladash Date: Sat, 19 Aug 2023 13:29:46 +0300 Subject: [PATCH 097/184] Fixup cellPadPeriphGetInfo --- rpcs3/Emu/Cell/Modules/cellPad.cpp | 146 +++++++++++++++++------------ rpcs3/Emu/Cell/Modules/cellPad.h | 15 ++- 2 files changed, 98 insertions(+), 63 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellPad.cpp b/rpcs3/Emu/Cell/Modules/cellPad.cpp index 71a1c54eef..dbe3baa5a1 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPad.cpp @@ -82,14 +82,61 @@ void cellPad_NotifyStateChange(u32 index, u32 state) return; } - const u32 old = info->reported_statuses[index]; + const auto handler = pad::get_current_handler(); + + const auto& pads = handler->GetPads(); + + const u32 old = info->reported_info[index].port_status; + + // Ignore sent status for now, use the latest instead + state = (pads[index]->m_port_status & CELL_PAD_STATUS_CONNECTED); if (~(old ^ state) & CELL_PAD_STATUS_CONNECTED) { return; } - info->reported_statuses[index] = (state & CELL_PAD_STATUS_CONNECTED) | CELL_PAD_STATUS_ASSIGN_CHANGES; + info->reported_info[index].port_status = (state & CELL_PAD_STATUS_CONNECTED) | CELL_PAD_STATUS_ASSIGN_CHANGES; + info->reported_info[index].device_capability = pads[index]->m_device_capability; + info->reported_info[index].device_type = pads[index]->m_device_type; + info->reported_info[index].pclass_type = pads[index]->m_class_type; + info->reported_info[index].pclass_profile = pads[index]->m_class_profile; + + if (pads[index]->m_vendor_id == 0 || pads[index]->m_product_id == 0) + { + // Fallback to defaults + + input::product_info product; + + switch (pads[index]->m_class_type) + { + case CELL_PAD_PCLASS_TYPE_GUITAR: + product = input::get_product_info(input::product_type::red_octane_gh_guitar); + break; + case CELL_PAD_PCLASS_TYPE_DRUM: + product = input::get_product_info(input::product_type::red_octane_gh_drum_kit); + break; + case CELL_PAD_PCLASS_TYPE_DJ: + product = input::get_product_info(input::product_type::dj_hero_turntable); + break; + case CELL_PAD_PCLASS_TYPE_DANCEMAT: + product = input::get_product_info(input::product_type::dance_dance_revolution_mat); + break; + case CELL_PAD_PCLASS_TYPE_NAVIGATION: + case CELL_PAD_PCLASS_TYPE_STANDARD: + default: + product = input::get_product_info(input::product_type::playstation_3_controller); + break; + } + + info->reported_info[index].vendor_id = product.vendor_id; + info->reported_info[index].product_id = product.product_id; + } + else + { + info->reported_info[index].vendor_id = pads[index]->m_vendor_id; + info->reported_info[index].product_id = pads[index]->m_product_id; + } } extern void pad_state_notify_state_change(u32 index, u32 state) @@ -115,7 +162,7 @@ error_code cellPadInit(ppu_thread& ppu, u32 max_connect) config.max_connect = max_connect; config.port_setting.fill(CELL_PAD_SETTING_PRESS_OFF | CELL_PAD_SETTING_SENSOR_OFF); - config.reported_statuses = {}; + config.reported_info = {}; std::array statuses{}; @@ -459,25 +506,36 @@ error_code cellPadPeriphGetInfo(vm::ptr info) std::memset(info.get_ptr(), 0, sizeof(CellPadPeriphInfo)); info->max_connect = config.max_connect; - info->now_connect = rinfo.now_connect; info->system_info = rinfo.system_info; const auto& pads = handler->GetPads(); + u32 now_connect = 0; + for (u32 i = 0; i < CELL_PAD_MAX_PORT_NUM; ++i) { if (i >= config.get_max_connect()) break; - info->port_status[i] = pads[i]->m_port_status; - pads[i]->m_port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; + info->port_status[i] = config.reported_info[i].port_status; + config.reported_info[i].port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; info->port_setting[i] = config.port_setting[i]; - info->device_capability[i] = pads[i]->m_device_capability; - info->device_type[i] = pads[i]->m_device_type; - info->pclass_type[i] = pads[i]->m_class_type; - info->pclass_profile[i] = pads[i]->m_class_profile; + + if (~config.reported_info[i].port_status & CELL_PAD_STATUS_CONNECTED) + { + continue; + } + + info->device_capability[i] = config.reported_info[i].device_capability; + info->device_type[i] = config.reported_info[i].device_type; + info->pclass_type[i] = config.reported_info[i].pclass_type; + info->pclass_profile[i] = config.reported_info[i].pclass_profile; + + now_connect++; } + info->now_connect = now_connect; + return CELL_OK; } @@ -649,52 +707,18 @@ error_code cellPadGetInfo(vm::ptr info) if (i >= config.get_max_connect()) break; - if (!config.is_reportedly_connected(i)) + config.reported_info[i].port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; // TODO: should ASSIGN flags be cleared here? + info->status[i] = config.reported_info[i].port_status; + + if (~config.reported_info[i].port_status & CELL_PAD_STATUS_CONNECTED) + { continue; - - config.reported_statuses[i] &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; // TODO: should ASSIGN flags be cleared here? - info->status[i] = config.reported_statuses[i]; - - if (config.reported_statuses[i] & CELL_PAD_STATUS_CONNECTED) - { - now_connect++; } - if (pads[i]->m_vendor_id == 0 || pads[i]->m_product_id == 0) - { - // Fallback to defaults + info->vendor_id[i] = config.reported_info[i].vendor_id; + info->product_id[i] = config.reported_info[i].product_id; - input::product_info product; - - switch (pads[i]->m_class_type) - { - case CELL_PAD_PCLASS_TYPE_GUITAR: - product = input::get_product_info(input::product_type::red_octane_gh_guitar); - break; - case CELL_PAD_PCLASS_TYPE_DRUM: - product = input::get_product_info(input::product_type::red_octane_gh_drum_kit); - break; - case CELL_PAD_PCLASS_TYPE_DJ: - product = input::get_product_info(input::product_type::dj_hero_turntable); - break; - case CELL_PAD_PCLASS_TYPE_DANCEMAT: - product = input::get_product_info(input::product_type::dance_dance_revolution_mat); - break; - case CELL_PAD_PCLASS_TYPE_NAVIGATION: - case CELL_PAD_PCLASS_TYPE_STANDARD: - default: - product = input::get_product_info(input::product_type::playstation_3_controller); - break; - } - - info->vendor_id[i] = product.vendor_id; - info->product_id[i] = product.product_id; - } - else - { - info->vendor_id[i] = pads[i]->m_vendor_id; - info->product_id[i] = pads[i]->m_product_id; - } + now_connect++; } info->now_connect = now_connect; @@ -732,19 +756,19 @@ error_code cellPadGetInfo2(vm::ptr info) if (i >= config.get_max_connect()) break; - if (!config.is_reportedly_connected(i)) - continue; - - info->port_status[i] = config.reported_statuses[i]; - config.reported_statuses[i] &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; + info->port_status[i] = config.reported_info[i].port_status; + config.reported_info[i].port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; info->port_setting[i] = config.port_setting[i]; + + if (~config.reported_info[i].port_status & CELL_PAD_STATUS_CONNECTED) + { + continue; + } + info->device_capability[i] = pads[i]->m_device_capability; info->device_type[i] = pads[i]->m_device_type; - if (config.reported_statuses[i] & CELL_PAD_STATUS_CONNECTED) - { - now_connect++; - } + now_connect++; } info->now_connect = now_connect; diff --git a/rpcs3/Emu/Cell/Modules/cellPad.h b/rpcs3/Emu/Cell/Modules/cellPad.h index f4603a80e6..80d6d48370 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.h +++ b/rpcs3/Emu/Cell/Modules/cellPad.h @@ -126,6 +126,17 @@ enum CELL_PADFILTER_IIR_CUTOFF_2ND_LPF_BT_010 = 2, // 10% Nyquist frequency }; +struct pad_data_internal +{ + u16 vendor_id; + u16 product_id; + u32 port_status; + u32 device_capability; + u32 device_type; + u32 pclass_type; + u32 pclass_profile; +}; + struct CellPadInfo { be_t max_connect; @@ -192,7 +203,7 @@ struct pad_info { atomic_t max_connect = 0; std::array port_setting{ 0 }; - std::array reported_statuses{}; + std::array reported_info{}; SAVESTATE_INIT_POS(11); @@ -209,7 +220,7 @@ struct pad_info // This result relies on data updates from config events on a dedicated thread to receive them bool is_reportedly_connected(u32 port_no) const { - return port_no < get_max_connect() && !!(reported_statuses[port_no] & CELL_PAD_STATUS_CONNECTED); + return port_no < get_max_connect() && !!(reported_info[port_no].port_status & CELL_PAD_STATUS_CONNECTED); } }; From 66aa02a3829d2b1e284a99e426db5b817cf07ab3 Mon Sep 17 00:00:00 2001 From: oltolm Date: Sat, 19 Aug 2023 19:12:26 +0200 Subject: [PATCH 098/184] xaudio2: enable IID_IXAudio2Extension for mingw-w64 --- rpcs3/Emu/Audio/XAudio2/XAudio2Backend.cpp | 4 ---- rpcs3/Emu/Audio/XAudio2/XAudio2Backend.h | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.cpp b/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.cpp index c8fb8aa2e3..178d23989e 100644 --- a/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.cpp +++ b/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.cpp @@ -317,12 +317,9 @@ f64 XAudio2Backend::GetCallbackFrameLen() return _10ms; } -#if _MSC_VER Microsoft::WRL::ComPtr xaudio_ext{}; -#endif f64 min_latency{}; -#if _MSC_VER if (HRESULT hr = m_xaudio2_instance->QueryInterface(IID_IXAudio2Extension, std::bit_cast(xaudio_ext.GetAddressOf())); FAILED(hr)) { XAudio.error("QueryInterface() failed: %s (0x%08x)", std::system_category().message(hr), static_cast(hr)); @@ -337,7 +334,6 @@ f64 XAudio2Backend::GetCallbackFrameLen() min_latency = static_cast(samples_per_q) / freq; } } -#endif return std::max(min_latency, _10ms); // 10ms is the minimum for XAudio } diff --git a/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.h b/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.h index 4a0e68977a..8308ce0eaf 100644 --- a/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.h +++ b/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.h @@ -11,6 +11,7 @@ #ifdef _MSC_VER #include #else +#include #include #endif #include From eb978a74f2b5e9c5dd704810a4e7ab91bc2678c3 Mon Sep 17 00:00:00 2001 From: Darkhost1999 <60384196+Darkhost1999@users.noreply.github.com> Date: Sat, 19 Aug 2023 15:32:44 -0500 Subject: [PATCH 099/184] Update BUILDING.md (#14515) --- BUILDING.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/BUILDING.md b/BUILDING.md index 86da1c1fa5..5740a80dd0 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -11,7 +11,7 @@ Other instructions may be found [here](https://wiki.rpcs3.net/index.php?title=Bu * [Python 3.6+](https://www.python.org/downloads/) (add to PATH) * [Qt 6.5.2](https://www.qt.io/download-qt-installer) * [Visual Studio 2019](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community) -* [Vulkan SDK 1.3.224+](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html)) +* [Vulkan SDK 1.3.224](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.224. **Either add the** `QTDIR` **environment variable, e.g.** `\6.5.2\msvc2019_64\` **, or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2019)** @@ -22,7 +22,7 @@ These are the essentials tools to build RPCS3 on Linux. Some of them can be inst * Clang 12+ or GCC 11+ * [CMake 3.16.9+](https://www.cmake.org/download/) * [Qt 6.5.2](https://www.qt.io/download-qt-installer) -* [Vulkan SDK 1.3.224+](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html)) +* [Vulkan SDK 1.3.224](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.224. * [SDL2](https://github.com/libsdl-org/SDL/releases) (for the FAudio backend) **If you have an NVIDIA GPU, you may need to install the libglvnd package.** @@ -57,7 +57,7 @@ For Ubuntu systems, it is strongly recommended to use the PPA from [LunarG](http ``` . /etc/os-release wget -qO - https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add - -sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-1.2.198-$UBUNTU_CODENAME.list https://packages.lunarg.com/vulkan/1.2.198/lunarg-vulkan-1.2.198-$UBUNTU_CODENAME.list +sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-1.3.224-$UBUNTU_CODENAME.list https://packages.lunarg.com/vulkan/1.3.224/lunarg-vulkan-1.2.198-$UBUNTU_CODENAME.list sudo apt update sudo apt install vulkan-sdk ``` From 78f2d44a0ef18944d46e55a52c5d048dd89918d2 Mon Sep 17 00:00:00 2001 From: nkarl7 Date: Sun, 20 Aug 2023 17:51:24 +0200 Subject: [PATCH 100/184] CLI - install multiple pkgs from folder (#14516) --- rpcs3/rpcs3qt/main_window.cpp | 141 ++++++++++++++++++++-------------- 1 file changed, 84 insertions(+), 57 deletions(-) diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 1aca508ce2..03e9b2de39 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -743,75 +743,102 @@ bool main_window::InstallPackages(QStringList file_paths, bool from_boot) m_gui_settings->SetValue(gui::fd_install_pkg, file_info.path()); } - if (file_paths.count() == 1 && file_paths.front().endsWith(".pkg", Qt::CaseInsensitive)) + if (file_paths.count() == 1) { const QString file_path = file_paths.front(); const QFileInfo file_info(file_path); - compat::package_info info = game_compatibility::GetPkgInfo(file_path, m_game_list_frame ? m_game_list_frame->GetGameCompatibility() : nullptr); - - if (!info.is_valid) + if (file_info.isDir()) { - QMessageBox::warning(this, tr("Invalid package!"), tr("The selected package is invalid!\n\nPath:\n%0").arg(file_path)); - return false; - } + gui_log.notice("PKG: Trying to install packages from dir: '%s'", file_path); - if (info.type != compat::package_type::other) - { - if (info.type == compat::package_type::dlc) + const QDir dir(file_path); + const auto dir_file_infos = dir.entryInfoList(QDir::Files); + + if (dir_file_infos.empty()) { - info.local_cat = tr("\nDLC", "Block for package type (DLC)"); - } - else - { - info.local_cat = tr("\nUpdate", "Block for package type (Update)"); - } - } - else if (!info.local_cat.isEmpty()) - { - info.local_cat = tr("\n%0", "Block for package type").arg(info.local_cat); - } - - if (!info.title_id.isEmpty()) - { - info.title_id = tr("\n%0", "Block for Title ID").arg(info.title_id); - } - - if (!info.version.isEmpty()) - { - info.version = tr("\nVersion %0", "Block for Version").arg(info.version); - } - - if (!info.changelog.isEmpty()) - { - info.changelog = tr("Changelog:\n%0", "Block for Changelog").arg(info.changelog); - } - - const QString info_string = QStringLiteral("%0\n\n%1%2%3%4").arg(file_info.fileName()).arg(info.title).arg(info.local_cat).arg(info.title_id).arg(info.version); - QString message = tr("Do you want to install this package?\n\n%0").arg(info_string); - - QMessageBox mb(QMessageBox::Icon::Question, tr("PKG Decrypter / Installer"), message, QMessageBox::Yes | QMessageBox::No, this); - mb.setDefaultButton(QMessageBox::No); - - if (!info.changelog.isEmpty()) - { - mb.setInformativeText(tr("To see the changelog, please click \"Show Details\".")); - mb.setDetailedText(tr("%0").arg(info.changelog)); - - // Smartass hack to make the unresizeable message box wide enough for the changelog - const int log_width = QLabel(info.changelog).sizeHint().width(); - while (QLabel(message).sizeHint().width() < log_width) - { - message += " "; + gui_log.notice("PKG: Could not find any files in dir: '%s'", file_path); + return true; } - mb.setText(message); + const auto dir_file_infos_count = dir_file_infos.count(); + + QList dir_file_paths(dir_file_infos_count); + for (int i = 0; i < dir_file_infos_count; i++) + { + dir_file_paths[i] = dir_file_infos[i].absoluteFilePath(); + } + + return InstallPackages(dir_file_paths, from_boot); } - if (mb.exec() != QMessageBox::Yes) + if (file_info.suffix().compare("pkg", Qt::CaseInsensitive) == 0) { - gui_log.notice("PKG: Cancelled installation from drop.\n%s\n%s", info_string, info.changelog); - return true; + compat::package_info info = game_compatibility::GetPkgInfo(file_path, m_game_list_frame ? m_game_list_frame->GetGameCompatibility() : nullptr); + + if (!info.is_valid) + { + QMessageBox::warning(this, tr("Invalid package!"), tr("The selected package is invalid!\n\nPath:\n%0").arg(file_path)); + return false; + } + + if (info.type != compat::package_type::other) + { + if (info.type == compat::package_type::dlc) + { + info.local_cat = tr("\nDLC", "Block for package type (DLC)"); + } + else + { + info.local_cat = tr("\nUpdate", "Block for package type (Update)"); + } + } + else if (!info.local_cat.isEmpty()) + { + info.local_cat = tr("\n%0", "Block for package type").arg(info.local_cat); + } + + if (!info.title_id.isEmpty()) + { + info.title_id = tr("\n%0", "Block for Title ID").arg(info.title_id); + } + + if (!info.version.isEmpty()) + { + info.version = tr("\nVersion %0", "Block for Version").arg(info.version); + } + + if (!info.changelog.isEmpty()) + { + info.changelog = tr("Changelog:\n%0", "Block for Changelog").arg(info.changelog); + } + + const QString info_string = QStringLiteral("%0\n\n%1%2%3%4").arg(file_info.fileName()).arg(info.title).arg(info.local_cat).arg(info.title_id).arg(info.version); + QString message = tr("Do you want to install this package?\n\n%0").arg(info_string); + + QMessageBox mb(QMessageBox::Icon::Question, tr("PKG Decrypter / Installer"), message, QMessageBox::Yes | QMessageBox::No, this); + mb.setDefaultButton(QMessageBox::No); + + if (!info.changelog.isEmpty()) + { + mb.setInformativeText(tr("To see the changelog, please click \"Show Details\".")); + mb.setDetailedText(tr("%0").arg(info.changelog)); + + // Smartass hack to make the unresizeable message box wide enough for the changelog + const int log_width = QLabel(info.changelog).sizeHint().width(); + while (QLabel(message).sizeHint().width() < log_width) + { + message += " "; + } + + mb.setText(message); + } + + if (mb.exec() != QMessageBox::Yes) + { + gui_log.notice("PKG: Cancelled installation from drop.\n%s\n%s", info_string, info.changelog); + return true; + } } } From 17302a9422d68abedee3a6a9212c80f59d3b2662 Mon Sep 17 00:00:00 2001 From: Eladash Date: Sun, 20 Aug 2023 04:24:42 +0300 Subject: [PATCH 101/184] Debugger/PPU: Superior Callstack Detection --- rpcs3/Emu/Cell/PPUAnalyser.h | 16 +- rpcs3/Emu/Cell/PPUDisAsm.cpp | 7 +- rpcs3/Emu/Cell/PPUThread.cpp | 341 +++++++++++++++++++++++++++++-- rpcs3/Emu/Cell/SPUThread.cpp | 30 ++- rpcs3/rpcs3qt/debugger_frame.cpp | 2 +- 5 files changed, 362 insertions(+), 34 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUAnalyser.h b/rpcs3/Emu/Cell/PPUAnalyser.h index c2965267cb..ef02faa530 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.h +++ b/rpcs3/Emu/Cell/PPUAnalyser.h @@ -262,6 +262,8 @@ struct ppu_pattern_matrix // PPU Instruction Type struct ppu_itype { + static constexpr struct branch_tag{} branch{}; // Branch Instructions + enum type { UNK = 0, @@ -432,11 +434,8 @@ struct ppu_itype ADDIC, ADDI, ADDIS, - BC, SC, - B, MCRF, - BCLR, CRNOR, CRANDC, ISYNC, @@ -446,7 +445,6 @@ struct ppu_itype CREQV, CRORC, CROR, - BCCTR, RLWIMI, RLWINM, RLWNM, @@ -781,6 +779,11 @@ struct ppu_itype FCTID_, FCTIDZ_, FCFID_, + + B, // branch_tag first + BC, + BCLR, + BCCTR, // branch_tag last }; // Enable address-of operator for ppu_decoder<> @@ -788,6 +791,11 @@ struct ppu_itype { return value; } + + friend constexpr bool operator &(type value, branch_tag) + { + return value >= B && value <= BCCTR; + } }; struct ppu_iname diff --git a/rpcs3/Emu/Cell/PPUDisAsm.cpp b/rpcs3/Emu/Cell/PPUDisAsm.cpp index 3f3564bbe5..6505bc44a1 100644 --- a/rpcs3/Emu/Cell/PPUDisAsm.cpp +++ b/rpcs3/Emu/Cell/PPUDisAsm.cpp @@ -106,12 +106,7 @@ std::pair PPUDisAsm::try_get_const_op_gpr_value(u32 re const auto type = s_ppu_itype.decode(opcode); - auto is_branch = [](enum ppu_itype::type itype) - { - return itype == ppu_itype::BC || itype == ppu_itype::B || itype == ppu_itype::BCLR || itype == ppu_itype::BCCTR; - }; - - if (is_branch(type) || type == ppu_itype::UNK) + if (type & ppu_itype::branch || type == ppu_itype::UNK) { // TODO: Detect calls, ignore them if reg is a non-volatile register return {}; diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 3df05e8421..e84345adcb 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -1563,16 +1563,19 @@ std::vector> ppu_thread::dump_callstack_list() const std::vector> call_stack_list; - bool first = true; + bool is_first = true; + bool skip_single_frame = false; + + const u64 _lr = this->lr; + const u32 _cia = this->cia; + const u64 gpr0 = this->gpr[0]; for ( u64 sp = r1; sp % 0x10 == 0u && sp >= stack_min && sp <= stack_max - ppu_stack_start_offset; - first = false + is_first = false ) { - u64 addr = *vm::get_super_ptr(static_cast(sp + 16)); - auto is_invalid = [](u64 addr) { if (addr > u32{umax} || addr % 4 || !vm::check_addr(static_cast(addr), vm::page_executable)) @@ -1584,28 +1587,330 @@ std::vector> ppu_thread::dump_callstack_list() const return addr == g_fxo->get().func_addr(1, true); }; - if (is_invalid(addr)) + if (is_first && !is_invalid(_lr)) { - if (first) - { - // Function hasn't saved LR, could be because it's a leaf function - // Use LR directly instead - addr = lr; + // Detect functions with no stack or before LR has been stored - if (is_invalid(addr)) + // Tracking if instruction has already been passed through + // Instead of using map or set, use two vectors relative to CIA and resize as needed + std::vector> inst_neg; + std::vector> inst_pos; + + auto get_inst = [&](u32 pos) -> be_t& + { + static be_t s_inst_empty{}; + + if (pos < _cia) { - // Skip it, workaround - continue; + const u32 neg_dist = (_cia - pos - 4) / 4; + + if (neg_dist >= inst_neg.size()) + { + const u32 inst_bound = pos & -256; + + const usz old_size = inst_neg.size(); + const usz new_size = neg_dist + (pos - inst_bound) / 4 + 1; + + if (new_size >= 0x8000) + { + // Gross lower limit for the function (if it is that size it is unlikely that it is even a leaf function) + return s_inst_empty; + } + + inst_neg.resize(new_size); + + if (!vm::try_access(inst_bound, &inst_neg[old_size], (new_size - old_size) * sizeof(be_t), false)) + { + // Failure (this would be detected as failure by zeroes) + } + + // Reverse the array (because this buffer directs backwards in address) + + for (usz start = old_size, end = new_size - 1; start < end; start++, end--) + { + std::swap(inst_neg[start], inst_neg[end]); + } + } + + return inst_neg[neg_dist]; + } + + const u32 pos_dist = (pos - _cia) / 4; + + if (pos_dist >= inst_pos.size()) + { + const u32 inst_bound = utils::align(pos, 256); + + const usz old_size = inst_pos.size(); + const usz new_size = pos_dist + (inst_bound - pos) / 4 + 1; + + if (new_size >= 0x8000) + { + // Gross upper limit for the function (if it is that size it is unlikely that it is even a leaf function) + return s_inst_empty; + } + + inst_pos.resize(new_size); + + if (!vm::try_access(pos, &inst_pos[old_size], (new_size - old_size) * sizeof(be_t), false)) + { + // Failure (this would be detected as failure by zeroes) + } + } + + return inst_pos[pos_dist]; + }; + + bool upper_abort = false; + + struct context_t + { + u32 start_point; + bool maybe_leaf = false; // True if the function is leaf or at the very end/start of non-leaf + bool non_leaf = false; // Absolutely not a leaf + bool about_to_push_frame = false; // STDU incoming + bool about_to_store_lr = false; // Link is about to be stored on stack + bool about_to_pop_frame = false; // ADDI R1 is about to be issued + bool about_to_load_link = false; // MTLR is about to be issued + bool maybe_use_reg0_instead_of_lr = false; // Use R0 at the end of a non-leaf function if ADDI has been issued before MTLR + }; + + // Start with CIA + std::deque workload{context_t{_cia}}; + + usz start = 0; + + for (; start < workload.size(); start++) + { + for (u32 wa = workload[start].start_point; vm::check_addr(wa, vm::page_executable);) + { + be_t& opcode = get_inst(wa); + + auto& [_, maybe_leaf, non_leaf, about_to_push_frame, about_to_store_lr, + about_to_pop_frame, about_to_load_link, maybe_use_reg0_instead_of_lr] = workload[start]; + + if (!opcode) + { + // Already passed or failure of reading + break; + } + + const ppu_opcode_t op{opcode}; + + // Mark as passed through + opcode = 0; + + const auto type = g_ppu_itype.decode(op.opcode); + + if (workload.size() == 1 && type == ppu_itype::STDU && op.rs == 1u && op.ra == 1u) + { + if (op.simm16 >= 0) + { + // Against ABI + non_leaf = true; + upper_abort = true; + break; + } + + // Saving LR to register: this is indeed a new function (ok because LR has not been saved yet) + maybe_leaf = true; + about_to_push_frame = true; + about_to_pop_frame = false; + upper_abort = true; + break; + } + + if (workload.size() == 1 && type == ppu_itype::STD && op.ra == 1u && op.rs == 0u) + { + bool found_matching_stdu = false; + + for (u32 back = 1; back < 20; back++) + { + be_t& opcode = get_inst(utils::sub_saturate(_cia, back * 4)); + + if (!opcode) + { + // Already passed or failure of reading + break; + } + + const ppu_opcode_t test_op{opcode}; + + const auto type = g_ppu_itype.decode(test_op.opcode); + + const u32 spr = ((test_op.spr >> 5) | ((test_op.spr & 0x1f) << 5)); + + if (type == ppu_itype::BCLR) + { + break; + } + + if (type == ppu_itype::STDU && test_op.rs == 1u && test_op.ra == 1u) + { + if (0 - (test_op.ds << 2) == (op.ds << 2) - 0x10) + { + found_matching_stdu = true; + } + + break; + } + } + + if (found_matching_stdu) + { + // Saving LR to stack: this is indeed a new function (ok because LR has not been saved yet) + maybe_leaf = true; + about_to_store_lr = true; + about_to_pop_frame = true; + upper_abort = true; + break; + } + } + + const u32 spr = ((op.spr >> 5) | ((op.spr & 0x1f) << 5)); + + // It can be placed before or after STDU, ignore for now + // if (workload.size() == 1 && type == ppu_itype::MFSPR && op.rs == 0u && spr == 0x8) + // { + // // Saving LR to register: this is indeed a new function (ok because LR has not been saved yet) + // maybe_leaf = true; + // about_to_store_lr = true; + // about_to_pop_frame = true; + // } + + if (type == ppu_itype::MTSPR && spr == 0x8 && op.rs == 0u) + { + // Test for special case: if ADDI R1 is not found later in code, it means that LR is not restored and R0 should be used instead + // Can also search for ADDI R1 backwards and pull the value from stack (needs more research if it is more reliable) + maybe_use_reg0_instead_of_lr = true; + } + + if (type == ppu_itype::UNK) + { + // Ignore for now + break; + } + + if ((type & ppu_itype::branch) && op.lk) + { + // Gave up on LR before saving + non_leaf = true; + about_to_pop_frame = true; + upper_abort = true; + break; + } + + // Even if BCLR is conditional, it still counts because LR value is ready for return + if (type == ppu_itype::BCLR) + { + // Returned + maybe_leaf = true; + upper_abort = true; + break; + } + + if (type == ppu_itype::ADDI && op.ra == 1u && op.rd == 1u) + { + if (op.simm16 < 0) + { + // Against ABI + non_leaf = true; + upper_abort = true; + break; + } + else if (op.simm16 > 0) + { + // Remember that SP is about to be restored + about_to_pop_frame = true; + non_leaf = true; + upper_abort = true; + break; + } + } + + const auto results = op_branch_targets(wa, op); + + bool proceeded = false; + + for (usz res_i = 0; res_i < results.size(); res_i++) + { + const u32 route_pc = results[res_i]; + + if (route_pc == umax) + { + continue; + } + + if (vm::check_addr(route_pc, vm::page_executable) && get_inst(route_pc)) + { + if (proceeded) + { + // Remember next route start point + workload.push_back(context_t{route_pc}); + } + else + { + // Next PC + wa = route_pc; + proceeded = true; + } + } + } + } + + if (upper_abort) + { + break; } } - else + + const context_t& res = workload[start]; + + if (res.maybe_leaf && !res.non_leaf) { - break; + const u32 result = res.maybe_use_reg0_instead_of_lr ? static_cast(gpr0) : static_cast(_lr); + + // Same stack as far as we know + call_stack_list.emplace_back(result, static_cast(sp)); + + if (res.about_to_store_lr) + { + // LR has yet to be stored on stack, ignore the stack value + skip_single_frame = true; + } + } + + if (res.about_to_pop_frame || (res.maybe_leaf && !res.non_leaf)) + { + const u64 temp_sp = *vm::get_super_ptr(static_cast(sp)); + + if (temp_sp <= sp) + { + // Ensure inequality and that the old stack pointer is higher than current + break; + } + + // Read the first stack frame so caller addresses can be obtained + sp = temp_sp; + continue; } } - // TODO: function addresses too - call_stack_list.emplace_back(static_cast(addr), static_cast(sp)); + u64 addr = *vm::get_super_ptr(static_cast(sp + 16)); + + if (skip_single_frame) + { + skip_single_frame = false; + } + else if (!is_invalid(addr)) + { + // TODO: function addresses too + call_stack_list.emplace_back(static_cast(addr), static_cast(sp)); + } + else if (!is_first) + { + break; + } const u64 temp_sp = *vm::get_super_ptr(static_cast(sp)); @@ -1616,6 +1921,8 @@ std::vector> ppu_thread::dump_callstack_list() const } sp = temp_sp; + + is_first = false; } return call_stack_list; diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 64747776b7..8ab60cec6c 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -1276,6 +1276,7 @@ std::vector> spu_thread::dump_callstack_list() const bool first = true; const v128 gpr0 = gpr[0]; + const u32 _pc = pc; // Declare first 128-bytes as invalid for stack (common values such as 0 do not make sense here) for (u32 sp = gpr[1]._u32[3]; (sp & 0xF) == 0u && sp >= 0x80u && sp <= 0x3FFE0u; first = false) @@ -1299,8 +1300,10 @@ std::vector> spu_thread::dump_callstack_list() const if (first && lr._u32[3] != gpr0._u32[3] && !is_invalid(gpr0)) { // Detect functions with no stack or before LR has been stored - std::vector passed(SPU_LS_SIZE / 4); - std::vector start_points{pc}; + std::vector passed(_pc / 4); + + // Start with PC + std::basic_string start_points{_pc}; bool is_ok = false; bool all_failed = false; @@ -1309,7 +1312,11 @@ std::vector> spu_thread::dump_callstack_list() const { for (u32 i = start_points[start]; i < SPU_LS_SIZE;) { - if (passed[i / 4]) + if (i / 4 >= passed.size()) + { + passed.resize(i / 4 + 1); + } + else if (passed[i / 4]) { // Already passed break; @@ -1322,7 +1329,7 @@ std::vector> spu_thread::dump_callstack_list() const if (start == 0 && type == spu_itype::STQD && op.ra == 1u && op.rt == 0u) { - // Saving LR to stack: this is indeed a new function + // Saving LR to stack: this is indeed a new function (ok because LR has not been saved yet) is_ok = true; break; } @@ -1360,12 +1367,23 @@ std::vector> spu_thread::dump_callstack_list() const for (usz res_i = 0; res_i < results.size(); res_i++) { const u32 route_pc = results[res_i]; - if (route_pc < SPU_LS_SIZE && !passed[route_pc / 4]) + + if (route_pc >= SPU_LS_SIZE) + { + continue; + } + + if (route_pc / 4 >= passed.size()) + { + passed.resize(route_pc / 4 + 1); + } + + if (!passed[route_pc / 4]) { if (proceeded) { // Remember next route start point - start_points.emplace_back(route_pc); + start_points.push_back(route_pc); } else { diff --git a/rpcs3/rpcs3qt/debugger_frame.cpp b/rpcs3/rpcs3qt/debugger_frame.cpp index 3d7e7a35bf..19bbe6fa2c 100644 --- a/rpcs3/rpcs3qt/debugger_frame.cpp +++ b/rpcs3/rpcs3qt/debugger_frame.cpp @@ -1330,7 +1330,7 @@ void debugger_frame::DoStep(bool step_over) ppu_opcode_t ppu_op{result}; const ppu_itype::type itype = g_ppu_itype.decode(ppu_op.opcode); - should_step_over = (itype == ppu_itype::BC || itype == ppu_itype::B || itype == ppu_itype::BCCTR || itype == ppu_itype::BCLR) && ppu_op.lk; + should_step_over = (itype & ppu_itype::branch && ppu_op.lk); } } From 7a0185dbcccdd18fec795c6cf38558b365b3b305 Mon Sep 17 00:00:00 2001 From: Eladash Date: Mon, 21 Aug 2023 12:43:05 +0300 Subject: [PATCH 102/184] PPU/debugger: Fixup --- rpcs3/Emu/Cell/PPUThread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index e84345adcb..b0aac9b280 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -1864,7 +1864,7 @@ std::vector> ppu_thread::dump_callstack_list() const } } - const context_t& res = workload[start]; + const context_t& res = workload[std::min(start, workload.size() - 1)]; if (res.maybe_leaf && !res.non_leaf) { From 1843a27c2a959324ff1d8552406bbef935ecf716 Mon Sep 17 00:00:00 2001 From: Eladash Date: Mon, 21 Aug 2023 12:43:53 +0300 Subject: [PATCH 103/184] LV2/Loader: Fix kernel regions addresses --- rpcs3/Emu/Cell/PPUModule.cpp | 23 +++++++++++++++++++---- rpcs3/Emu/Cell/lv2/sys_memory.cpp | 10 ++++++++-- rpcs3/Emu/Memory/vm.cpp | 8 ++++---- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index ba4a063d55..64e197e3e3 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -1860,15 +1860,30 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str else if (already_loaded) { } - else if (!vm::falloc(addr, size, vm::main)) + else if (![&]() -> bool { - ppu_loader.error("vm::falloc(vm::main) failed (addr=0x%x, memsz=0x%x)", addr, size); // TODO + // 1M pages if it is RSX shared + const u32 area_flags = (_seg.flags >> 28) ? vm::page_size_1m : vm::page_size_64k; + const u32 alloc_at = std::max(addr & -0x10000000, 0x10000); - if (!vm::falloc(addr, size)) + const auto area = vm::reserve_map(vm::any, std::max(addr & -0x10000000, 0x10000), 0x10000000, area_flags); + + if (!area) { - ppu_loader.error("ppu_load_exec(): vm::falloc() failed (addr=0x%x, memsz=0x%x)", addr, size); return false; } + + if (area->addr != alloc_at || (area->flags & 0xf00) != area_flags) + { + ppu_loader.error("Failed to allocate memory at 0x%x - conflicting memory area exists: area->addr=0x%x, area->flags=0x%x", addr, area->addr, area->flags); + return false; + } + + return area->falloc(addr, size); + }()) + { + ppu_loader.error("ppu_load_exec(): vm::falloc() failed (addr=0x%x, memsz=0x%x)", addr, size); + return false; } // Store only LOAD segments (TODO) diff --git a/rpcs3/Emu/Cell/lv2/sys_memory.cpp b/rpcs3/Emu/Cell/lv2/sys_memory.cpp index 067902d60c..d8b9ff6823 100644 --- a/rpcs3/Emu/Cell/lv2/sys_memory.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_memory.cpp @@ -85,6 +85,12 @@ struct sys_memory_address_table } }; +std::shared_ptr reserve_map(u32 alloc_size, u32 align) +{ + return vm::reserve_map(align == 0x10000 ? vm::user64k : vm::user1m, 0, align == 0x10000 ? 0x20000000 : utils::align(alloc_size, 0x10000000) + , align == 0x10000 ? (vm::page_size_64k | vm::bf0_0x1) : (vm::page_size_1m | vm::bf0_0x1)); +} + // Todo: fix order of error checks error_code sys_memory_allocate(cpu_thread& cpu, u32 size, u64 flags, vm::ptr alloc_addr) @@ -123,7 +129,7 @@ error_code sys_memory_allocate(cpu_thread& cpu, u32 size, u64 flags, vm::ptralloc(size, nullptr, align)) { @@ -197,7 +203,7 @@ error_code sys_memory_allocate_from_container(cpu_thread& cpu, u32 size, u32 cid return ct.ret; } - if (const auto area = vm::reserve_map(align == 0x10000 ? vm::user64k : vm::user1m, 0, utils::align(size, 0x10000000), 0x401)) + if (const auto area = reserve_map(size, align)) { if (const u32 addr = area->alloc(size)) { diff --git a/rpcs3/Emu/Memory/vm.cpp b/rpcs3/Emu/Memory/vm.cpp index 28305052a1..79680c5c41 100644 --- a/rpcs3/Emu/Memory/vm.cpp +++ b/rpcs3/Emu/Memory/vm.cpp @@ -1803,12 +1803,12 @@ namespace vm { const u32 max = (0xC0000000 - size) & (0 - align); - if (size > 0xC0000000 - 0x20000000 || max < 0x20000000) + if (size > 0xC0000000 - 0x10000000 || max < 0x10000000) { return nullptr; } - for (u32 addr = utils::align(0x20000000, align);; addr += align) + for (u32 addr = utils::align(0x10000000, align);; addr += align) { if (_test_map(addr, size)) { @@ -2131,8 +2131,8 @@ namespace vm g_locations = { - std::make_shared(0x00010000, 0x1FFF0000, page_size_64k | preallocated), // main - std::make_shared(0x20000000, 0x10000000, page_size_64k | bf0_0x1), // user 64k pages + std::make_shared(0x00010000, 0x0FFF0000, page_size_64k | preallocated), // main + nullptr, // user 64k pages nullptr, // user 1m pages nullptr, // rsx context std::make_shared(0xC0000000, 0x10000000, page_size_64k | preallocated), // video From 16c8f8c9cdd398bc0294afe309de750b9787abab Mon Sep 17 00:00:00 2001 From: Eladash Date: Mon, 21 Aug 2023 15:55:36 +0300 Subject: [PATCH 104/184] Game List: Add initials-only search --- rpcs3/rpcs3qt/game_list_frame.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index 4acf9649da..b8e66edadb 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -2446,6 +2446,30 @@ bool game_list_frame::SearchMatchesApp(const QString& name, const QString& seria { return true; } + + // Initials-only search + if (search_text.size() >= 2 && search_text.count(QRegularExpression(QStringLiteral("[a-z0-9]"))) >= 2 && !search_text.contains(QRegularExpression(QStringLiteral("[^a-z0-9 ]")))) + { + QString initials = QStringLiteral("\\b"); + + for (auto it = search_text.begin(); it != search_text.end(); it++) + { + if (it->isSpace()) + { + continue; + } + + initials += *it; + initials += QStringLiteral("\\w*\\b "); + } + + initials += QChar('?'); + + if (title_name_replaced_trademarks_with_spaces.contains(QRegularExpression(initials))) + { + return true; + } + } } return title_name.contains(search_text) || serial.toLower().contains(search_text); From f3b631fbb4c1ed0e4696e9d2113a0b1aa77d71ed Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 21 Aug 2023 22:18:58 +0200 Subject: [PATCH 105/184] qt/utils: support full path in get_dir_entries --- rpcs3/rpcs3qt/main_window.cpp | 14 +++----------- rpcs3/rpcs3qt/qt_utils.cpp | 6 +++--- rpcs3/rpcs3qt/qt_utils.h | 2 +- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 03e9b2de39..a99fec89f8 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -753,22 +753,14 @@ bool main_window::InstallPackages(QStringList file_paths, bool from_boot) gui_log.notice("PKG: Trying to install packages from dir: '%s'", file_path); const QDir dir(file_path); - const auto dir_file_infos = dir.entryInfoList(QDir::Files); + const QStringList dir_file_paths = gui::utils::get_dir_entries(dir, {}, true); - if (dir_file_infos.empty()) + if (dir_file_paths.empty()) { gui_log.notice("PKG: Could not find any files in dir: '%s'", file_path); return true; } - const auto dir_file_infos_count = dir_file_infos.count(); - - QList dir_file_paths(dir_file_infos_count); - for (int i = 0; i < dir_file_infos_count; i++) - { - dir_file_paths[i] = dir_file_infos[i].absoluteFilePath(); - } - return InstallPackages(dir_file_paths, from_boot); } @@ -848,7 +840,7 @@ bool main_window::InstallPackages(QStringList file_paths, bool from_boot) const auto install_filetype = [&installed_rap_and_edat_count, &file_paths](const std::string extension) { const QString pattern = QString(".*\\.%1").arg(QString::fromStdString(extension)); - for (const auto& file : file_paths.filter(QRegularExpression(pattern, QRegularExpression::PatternOption::CaseInsensitiveOption))) + for (const QString& file : file_paths.filter(QRegularExpression(pattern, QRegularExpression::PatternOption::CaseInsensitiveOption))) { const QFileInfo file_info(file); const std::string filename = sstr(file_info.fileName()); diff --git a/rpcs3/rpcs3qt/qt_utils.cpp b/rpcs3/rpcs3qt/qt_utils.cpp index 3f2023e393..261676f919 100644 --- a/rpcs3/rpcs3qt/qt_utils.cpp +++ b/rpcs3/rpcs3qt/qt_utils.cpp @@ -164,13 +164,13 @@ namespace gui return icon; } - QStringList get_dir_entries(const QDir& dir, const QStringList& name_filters) + QStringList get_dir_entries(const QDir& dir, const QStringList& name_filters, bool full_path) { QFileInfoList entries = dir.entryInfoList(name_filters, QDir::Files); QStringList res; - for (const QFileInfo &entry : entries) + for (const QFileInfo& entry : entries) { - res.append(entry.baseName()); + res.append(full_path ? entry.absoluteFilePath() : entry.baseName()); } return res; } diff --git a/rpcs3/rpcs3qt/qt_utils.h b/rpcs3/rpcs3qt/qt_utils.h index 197898a5be..6607dca350 100644 --- a/rpcs3/rpcs3qt/qt_utils.h +++ b/rpcs3/rpcs3qt/qt_utils.h @@ -63,7 +63,7 @@ namespace gui QIcon get_colorized_icon(const QIcon& old_icon, const QColor& old_color, const std::map& new_colors, bool use_special_masks = false, bool colorize_all = false); // Returns a list of all base names of files in dir whose complete file names contain one of the given name_filters - QStringList get_dir_entries(const QDir& dir, const QStringList& name_filters); + QStringList get_dir_entries(const QDir& dir, const QStringList& name_filters, bool full_path = false); // Returns the color specified by its color_role for the QLabels with object_name QColor get_label_color(const QString& object_name, QPalette::ColorRole color_role = QPalette::WindowText); From e28b705f0d2af9a984db5ca65cca44204977577a Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 21 Aug 2023 21:14:42 +0200 Subject: [PATCH 106/184] Input: Add PS Move navigation controller Add the ps move navigation controller to pad types. Use proper pad class profiles. --- rpcs3/Emu/Cell/Modules/cellPad.cpp | 2 + rpcs3/Emu/Cell/Modules/cellPad.h | 86 ------------- rpcs3/Emu/Io/pad_types.h | 86 +++++++++++++ rpcs3/Input/product_info.h | 175 +++++++++++++++++++++++--- rpcs3/rpcs3qt/pad_settings_dialog.cpp | 7 +- 5 files changed, 249 insertions(+), 107 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellPad.cpp b/rpcs3/Emu/Cell/Modules/cellPad.cpp index dbe3baa5a1..03658b8645 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPad.cpp @@ -123,6 +123,8 @@ void cellPad_NotifyStateChange(u32 index, u32 state) product = input::get_product_info(input::product_type::dance_dance_revolution_mat); break; case CELL_PAD_PCLASS_TYPE_NAVIGATION: + product = input::get_product_info(input::product_type::ps_move_navigation); + break; case CELL_PAD_PCLASS_TYPE_STANDARD: default: product = input::get_product_info(input::product_type::playstation_3_controller); diff --git a/rpcs3/Emu/Cell/Modules/cellPad.h b/rpcs3/Emu/Cell/Modules/cellPad.h index 80d6d48370..6219ada00a 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.h +++ b/rpcs3/Emu/Cell/Modules/cellPad.h @@ -24,92 +24,6 @@ enum CellPadFilterError : u32 CELL_PADFILTER_ERROR_INVALID_PARAMETER = 0x80121401, }; -// Controller types -enum -{ - CELL_PAD_PCLASS_TYPE_STANDARD = 0x00, - CELL_PAD_PCLASS_TYPE_GUITAR = 0x01, - CELL_PAD_PCLASS_TYPE_DRUM = 0x02, - CELL_PAD_PCLASS_TYPE_DJ = 0x03, - CELL_PAD_PCLASS_TYPE_DANCEMAT = 0x04, - CELL_PAD_PCLASS_TYPE_NAVIGATION = 0x05, -}; - -// Profile of a Standard Type Controller -// Profile of a Navigation Type Controller -// Bits 0 – 31 All 0s - -// Profile of a Guitar Type Controller -enum -{ - // Basic - CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_1 = 0x00000001, - CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_2 = 0x00000002, - CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_3 = 0x00000004, - CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_4 = 0x00000008, - CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_5 = 0x00000010, - CELL_PAD_PCLASS_PROFILE_GUITAR_STRUM_UP = 0x00000020, - CELL_PAD_PCLASS_PROFILE_GUITAR_STRUM_DOWN = 0x00000040, - CELL_PAD_PCLASS_PROFILE_GUITAR_WHAMMYBAR = 0x00000080, - // All Basic = 0x000000FF - - // Optional - CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H1 = 0x00000100, - CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H2 = 0x00000200, - CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H3 = 0x00000400, - CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H4 = 0x00000800, - CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H5 = 0x00001000, - CELL_PAD_PCLASS_PROFILE_GUITAR_5WAY_EFFECT = 0x00002000, - CELL_PAD_PCLASS_PROFILE_GUITAR_TILT_SENS = 0x00004000, - // All = 0x00007FFF -}; - -// Profile of a Drum Type Controller -enum -{ - CELL_PAD_PCLASS_PROFILE_DRUM_SNARE = 0x00000001, - CELL_PAD_PCLASS_PROFILE_DRUM_TOM = 0x00000002, - CELL_PAD_PCLASS_PROFILE_DRUM_TOM2 = 0x00000004, - CELL_PAD_PCLASS_PROFILE_DRUM_TOM_FLOOR = 0x00000008, - CELL_PAD_PCLASS_PROFILE_DRUM_KICK = 0x00000010, - CELL_PAD_PCLASS_PROFILE_DRUM_CYM_HiHAT = 0x00000020, - CELL_PAD_PCLASS_PROFILE_DRUM_CYM_CRASH = 0x00000040, - CELL_PAD_PCLASS_PROFILE_DRUM_CYM_RIDE = 0x00000080, - CELL_PAD_PCLASS_PROFILE_DRUM_KICK2 = 0x00000100, - // All = 0x000001FF -}; - -// Profile of a DJ Deck Type Controller -enum -{ - CELL_PAD_PCLASS_PROFILE_DJ_MIXER_ATTACK = 0x00000001, - CELL_PAD_PCLASS_PROFILE_DJ_MIXER_CROSSFADER = 0x00000002, - CELL_PAD_PCLASS_PROFILE_DJ_MIXER_DSP_DIAL = 0x00000004, - CELL_PAD_PCLASS_PROFILE_DJ_DECK1_STREAM1 = 0x00000008, - CELL_PAD_PCLASS_PROFILE_DJ_DECK1_STREAM2 = 0x00000010, - CELL_PAD_PCLASS_PROFILE_DJ_DECK1_STREAM3 = 0x00000020, - CELL_PAD_PCLASS_PROFILE_DJ_DECK1_PLATTER = 0x00000040, - CELL_PAD_PCLASS_PROFILE_DJ_DECK2_STREAM1 = 0x00000080, - CELL_PAD_PCLASS_PROFILE_DJ_DECK2_STREAM2 = 0x00000100, - CELL_PAD_PCLASS_PROFILE_DJ_DECK2_STREAM3 = 0x00000200, - CELL_PAD_PCLASS_PROFILE_DJ_DECK2_PLATTER = 0x00000400, - // All = 0x000007FF -}; - -// Profile of a Dance Mat Type Controller -enum -{ - CELL_PAD_PCLASS_PROFILE_DANCEMAT_CIRCLE = 0x00000001, - CELL_PAD_PCLASS_PROFILE_DANCEMAT_CROSS = 0x00000002, - CELL_PAD_PCLASS_PROFILE_DANCEMAT_TRIANGLE = 0x00000004, - CELL_PAD_PCLASS_PROFILE_DANCEMAT_SQUARE = 0x00000008, - CELL_PAD_PCLASS_PROFILE_DANCEMAT_RIGHT = 0x00000010, - CELL_PAD_PCLASS_PROFILE_DANCEMAT_LEFT = 0x00000020, - CELL_PAD_PCLASS_PROFILE_DANCEMAT_UP = 0x00000040, - CELL_PAD_PCLASS_PROFILE_DANCEMAT_DOWN = 0x00000080, - // All = 0x000000FF -}; - // Length returned in CellPadData struct enum { diff --git a/rpcs3/Emu/Io/pad_types.h b/rpcs3/Emu/Io/pad_types.h index 7d1f91d994..916f30a559 100644 --- a/rpcs3/Emu/Io/pad_types.h +++ b/rpcs3/Emu/Io/pad_types.h @@ -124,6 +124,92 @@ enum DeviceType CELL_PAD_DEV_TYPE_LDD = 5, }; +// Controller types +enum +{ + CELL_PAD_PCLASS_TYPE_STANDARD = 0x00, + CELL_PAD_PCLASS_TYPE_GUITAR = 0x01, + CELL_PAD_PCLASS_TYPE_DRUM = 0x02, + CELL_PAD_PCLASS_TYPE_DJ = 0x03, + CELL_PAD_PCLASS_TYPE_DANCEMAT = 0x04, + CELL_PAD_PCLASS_TYPE_NAVIGATION = 0x05, +}; + +// Profile of a Standard Type Controller +// Profile of a Navigation Type Controller +// Bits 0 – 31 All 0s + +// Profile of a Guitar Type Controller +enum +{ + // Basic + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_1 = 0x00000001, + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_2 = 0x00000002, + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_3 = 0x00000004, + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_4 = 0x00000008, + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_5 = 0x00000010, + CELL_PAD_PCLASS_PROFILE_GUITAR_STRUM_UP = 0x00000020, + CELL_PAD_PCLASS_PROFILE_GUITAR_STRUM_DOWN = 0x00000040, + CELL_PAD_PCLASS_PROFILE_GUITAR_WHAMMYBAR = 0x00000080, + // All Basic = 0x000000FF + + // Optional + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H1 = 0x00000100, + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H2 = 0x00000200, + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H3 = 0x00000400, + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H4 = 0x00000800, + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H5 = 0x00001000, + CELL_PAD_PCLASS_PROFILE_GUITAR_5WAY_EFFECT = 0x00002000, + CELL_PAD_PCLASS_PROFILE_GUITAR_TILT_SENS = 0x00004000, + // All = 0x00007FFF +}; + +// Profile of a Drum Type Controller +enum +{ + CELL_PAD_PCLASS_PROFILE_DRUM_SNARE = 0x00000001, + CELL_PAD_PCLASS_PROFILE_DRUM_TOM = 0x00000002, + CELL_PAD_PCLASS_PROFILE_DRUM_TOM2 = 0x00000004, + CELL_PAD_PCLASS_PROFILE_DRUM_TOM_FLOOR = 0x00000008, + CELL_PAD_PCLASS_PROFILE_DRUM_KICK = 0x00000010, + CELL_PAD_PCLASS_PROFILE_DRUM_CYM_HiHAT = 0x00000020, + CELL_PAD_PCLASS_PROFILE_DRUM_CYM_CRASH = 0x00000040, + CELL_PAD_PCLASS_PROFILE_DRUM_CYM_RIDE = 0x00000080, + CELL_PAD_PCLASS_PROFILE_DRUM_KICK2 = 0x00000100, + // All = 0x000001FF +}; + +// Profile of a DJ Deck Type Controller +enum +{ + CELL_PAD_PCLASS_PROFILE_DJ_MIXER_ATTACK = 0x00000001, + CELL_PAD_PCLASS_PROFILE_DJ_MIXER_CROSSFADER = 0x00000002, + CELL_PAD_PCLASS_PROFILE_DJ_MIXER_DSP_DIAL = 0x00000004, + CELL_PAD_PCLASS_PROFILE_DJ_DECK1_STREAM1 = 0x00000008, + CELL_PAD_PCLASS_PROFILE_DJ_DECK1_STREAM2 = 0x00000010, + CELL_PAD_PCLASS_PROFILE_DJ_DECK1_STREAM3 = 0x00000020, + CELL_PAD_PCLASS_PROFILE_DJ_DECK1_PLATTER = 0x00000040, + CELL_PAD_PCLASS_PROFILE_DJ_DECK2_STREAM1 = 0x00000080, + CELL_PAD_PCLASS_PROFILE_DJ_DECK2_STREAM2 = 0x00000100, + CELL_PAD_PCLASS_PROFILE_DJ_DECK2_STREAM3 = 0x00000200, + CELL_PAD_PCLASS_PROFILE_DJ_DECK2_PLATTER = 0x00000400, + // All = 0x000007FF +}; + +// Profile of a Dance Mat Type Controller +enum +{ + CELL_PAD_PCLASS_PROFILE_DANCEMAT_CIRCLE = 0x00000001, + CELL_PAD_PCLASS_PROFILE_DANCEMAT_CROSS = 0x00000002, + CELL_PAD_PCLASS_PROFILE_DANCEMAT_TRIANGLE = 0x00000004, + CELL_PAD_PCLASS_PROFILE_DANCEMAT_SQUARE = 0x00000008, + CELL_PAD_PCLASS_PROFILE_DANCEMAT_RIGHT = 0x00000010, + CELL_PAD_PCLASS_PROFILE_DANCEMAT_LEFT = 0x00000020, + CELL_PAD_PCLASS_PROFILE_DANCEMAT_UP = 0x00000040, + CELL_PAD_PCLASS_PROFILE_DANCEMAT_DOWN = 0x00000080, + // All = 0x000000FF +}; + enum ButtonDataOffset { CELL_PAD_BTN_OFFSET_DIGITAL1 = 2, diff --git a/rpcs3/Input/product_info.h b/rpcs3/Input/product_info.h index dfa30d189a..86ce0a09e4 100644 --- a/rpcs3/Input/product_info.h +++ b/rpcs3/Input/product_info.h @@ -1,6 +1,7 @@ #pragma once #include +#include "Emu/Io/pad_types.h" namespace input { @@ -14,7 +15,8 @@ namespace input harmonix_rockband_guitar, harmonix_rockband_drum_kit, harmonix_rockband_drum_kit_2, - rock_revolution_drum_kit + rock_revolution_drum_kit, + ps_move_navigation }; enum vendor_id @@ -35,14 +37,15 @@ namespace input harmonix_rockband_drum_kit_2 = 0x0218, // Harmonix Pro-Drum Kit (Rock Band III Pro-Drum Controller) playstation_3_controller = 0x0268, // PlayStation 3 Controller rock_revolution_drum_kit = 0x0300, // Rock Revolution Drum Controller + ps_move_navigation = 0x042F, // PlayStation Move navigation controller }; struct product_info { product_type type; - unsigned short vendor_id; - unsigned short product_id; - unsigned int pclass_profile; // See CELL_PAD_PCLASS_PROFILE flags + u16 vendor_id; + u16 product_id; + u32 pclass_profile; // See CELL_PAD_PCLASS_PROFILE flags }; inline product_info get_product_info(product_type type) @@ -51,40 +54,172 @@ namespace input { case product_type::dance_dance_revolution_mat: { - return product_info{ type, vendor_id::konami_de, product_id::dance_dance_revolution_mat, 0x000000FF }; + constexpr u32 profile = + CELL_PAD_PCLASS_PROFILE_DANCEMAT_CIRCLE | + CELL_PAD_PCLASS_PROFILE_DANCEMAT_CROSS | + CELL_PAD_PCLASS_PROFILE_DANCEMAT_TRIANGLE | + CELL_PAD_PCLASS_PROFILE_DANCEMAT_SQUARE | + CELL_PAD_PCLASS_PROFILE_DANCEMAT_RIGHT | + CELL_PAD_PCLASS_PROFILE_DANCEMAT_LEFT | + CELL_PAD_PCLASS_PROFILE_DANCEMAT_UP | + CELL_PAD_PCLASS_PROFILE_DANCEMAT_DOWN; + return product_info{ + .type = type, + .vendor_id = vendor_id::konami_de, + .product_id = product_id::dance_dance_revolution_mat, + .pclass_profile = profile + }; } case product_type::dj_hero_turntable: { - return product_info{ type, vendor_id::sony_cea, product_id::dj_hero_turntable, 0x000007FF }; + constexpr u32 profile = + CELL_PAD_PCLASS_PROFILE_DJ_MIXER_ATTACK | + CELL_PAD_PCLASS_PROFILE_DJ_MIXER_CROSSFADER | + CELL_PAD_PCLASS_PROFILE_DJ_MIXER_DSP_DIAL | + CELL_PAD_PCLASS_PROFILE_DJ_DECK1_STREAM1 | + CELL_PAD_PCLASS_PROFILE_DJ_DECK1_STREAM2 | + CELL_PAD_PCLASS_PROFILE_DJ_DECK1_STREAM3 | + CELL_PAD_PCLASS_PROFILE_DJ_DECK1_PLATTER | + CELL_PAD_PCLASS_PROFILE_DJ_DECK2_STREAM1 | + CELL_PAD_PCLASS_PROFILE_DJ_DECK2_STREAM2 | + CELL_PAD_PCLASS_PROFILE_DJ_DECK2_STREAM3 | + CELL_PAD_PCLASS_PROFILE_DJ_DECK2_PLATTER; + return product_info{ + .type = type, + .vendor_id = vendor_id::sony_cea, + .product_id = product_id::dj_hero_turntable, + .pclass_profile = profile + }; } case product_type::harmonix_rockband_drum_kit: { - return product_info{ type, vendor_id::sony_cea, product_id::harmonix_rockband_drum_kit, 0x000000FF }; + constexpr u32 profile = + CELL_PAD_PCLASS_PROFILE_DRUM_SNARE | + CELL_PAD_PCLASS_PROFILE_DRUM_TOM | + CELL_PAD_PCLASS_PROFILE_DRUM_TOM2 | + CELL_PAD_PCLASS_PROFILE_DRUM_TOM_FLOOR | + CELL_PAD_PCLASS_PROFILE_DRUM_KICK | + CELL_PAD_PCLASS_PROFILE_DRUM_CYM_HiHAT | + CELL_PAD_PCLASS_PROFILE_DRUM_CYM_CRASH | + CELL_PAD_PCLASS_PROFILE_DRUM_CYM_RIDE; + return product_info{ + .type = type, + .vendor_id = vendor_id::sony_cea, + .product_id = product_id::harmonix_rockband_drum_kit, + .pclass_profile = profile + }; } case product_type::harmonix_rockband_drum_kit_2: { - return product_info{ type, vendor_id::sony_cea, product_id::harmonix_rockband_drum_kit_2, 0x000000BF }; + constexpr u32 profile = + CELL_PAD_PCLASS_PROFILE_DRUM_SNARE | + CELL_PAD_PCLASS_PROFILE_DRUM_TOM | + CELL_PAD_PCLASS_PROFILE_DRUM_TOM2 | + CELL_PAD_PCLASS_PROFILE_DRUM_TOM_FLOOR | + CELL_PAD_PCLASS_PROFILE_DRUM_KICK | + CELL_PAD_PCLASS_PROFILE_DRUM_CYM_HiHAT | + CELL_PAD_PCLASS_PROFILE_DRUM_CYM_RIDE; + return product_info{ + .type = type, + .vendor_id = vendor_id::sony_cea, + .product_id = product_id::harmonix_rockband_drum_kit_2, + .pclass_profile = profile + }; } case product_type::harmonix_rockband_guitar: { - return product_info{ type, vendor_id::sony_cea, product_id::harmonix_rockband_guitar, 0x00007FFF }; + constexpr u32 profile = + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_1 | + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_2 | + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_3 | + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_4 | + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_5 | + CELL_PAD_PCLASS_PROFILE_GUITAR_STRUM_UP | + CELL_PAD_PCLASS_PROFILE_GUITAR_STRUM_DOWN | + CELL_PAD_PCLASS_PROFILE_GUITAR_WHAMMYBAR | + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H1 | + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H2 | + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H3 | + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H4 | + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H5 | + CELL_PAD_PCLASS_PROFILE_GUITAR_5WAY_EFFECT | + CELL_PAD_PCLASS_PROFILE_GUITAR_TILT_SENS; + return product_info{ + .type = type, + .vendor_id = vendor_id::sony_cea, + .product_id = product_id::harmonix_rockband_guitar, + .pclass_profile = profile + }; } case product_type::red_octane_gh_drum_kit: { - return product_info{ type, vendor_id::sony_cea, product_id::red_octane_gh_drum_kit, 0x000000BB }; + constexpr u32 profile = + CELL_PAD_PCLASS_PROFILE_DRUM_SNARE | + CELL_PAD_PCLASS_PROFILE_DRUM_TOM | + CELL_PAD_PCLASS_PROFILE_DRUM_TOM_FLOOR | + CELL_PAD_PCLASS_PROFILE_DRUM_KICK | + CELL_PAD_PCLASS_PROFILE_DRUM_CYM_HiHAT | + CELL_PAD_PCLASS_PROFILE_DRUM_CYM_RIDE; + return product_info{ + .type = type, + .vendor_id = vendor_id::sony_cea, + .product_id = product_id::red_octane_gh_drum_kit, + .pclass_profile = profile + }; } case product_type::red_octane_gh_guitar: { - return product_info{ type, vendor_id::sony_cea, product_id::red_octane_gh_guitar, 0x000000FF }; + constexpr u32 profile = + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_1 | + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_2 | + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_3 | + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_4 | + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_5 | + CELL_PAD_PCLASS_PROFILE_GUITAR_STRUM_UP | + CELL_PAD_PCLASS_PROFILE_GUITAR_STRUM_DOWN | + CELL_PAD_PCLASS_PROFILE_GUITAR_WHAMMYBAR; + return product_info{ + .type = type, + .vendor_id = vendor_id::sony_cea, + .product_id = product_id::red_octane_gh_guitar, + .pclass_profile = profile + }; } case product_type::rock_revolution_drum_kit: { - return product_info{ type, vendor_id::sony_cea, product_id::rock_revolution_drum_kit, 0x000000FB }; + constexpr u32 profile = + CELL_PAD_PCLASS_PROFILE_DRUM_SNARE | + CELL_PAD_PCLASS_PROFILE_DRUM_TOM | + CELL_PAD_PCLASS_PROFILE_DRUM_TOM_FLOOR | + CELL_PAD_PCLASS_PROFILE_DRUM_KICK | + CELL_PAD_PCLASS_PROFILE_DRUM_CYM_HiHAT | + CELL_PAD_PCLASS_PROFILE_DRUM_CYM_CRASH | + CELL_PAD_PCLASS_PROFILE_DRUM_CYM_RIDE; + return product_info{ + .type = type, + .vendor_id = vendor_id::sony_cea, + .product_id = product_id::rock_revolution_drum_kit, + .pclass_profile = profile + }; + } + case product_type::ps_move_navigation: + { + return product_info{ + .type = type, + .vendor_id = vendor_id::sony_corp, + .product_id = product_id::ps_move_navigation, + .pclass_profile = 0x0 + }; } case product_type::playstation_3_controller: default: // GCC doesn't like it when there is no return value if if all enum values are covered { - return product_info{ type, vendor_id::sony_corp, product_id::playstation_3_controller, 0x0 }; + return product_info{ + .type = type, + .vendor_id = vendor_id::sony_corp, + .product_id = product_id::playstation_3_controller, + .pclass_profile = 0x0 + }; } } } @@ -94,14 +229,14 @@ namespace input switch (class_id) { default: - case 0: // CELL_PAD_PCLASS_TYPE_STANDARD + case CELL_PAD_PCLASS_TYPE_STANDARD: { return { get_product_info(product_type::playstation_3_controller) }; } - case 1: // CELL_PAD_PCLASS_TYPE_GUITAR + case CELL_PAD_PCLASS_TYPE_GUITAR: { return { @@ -109,7 +244,7 @@ namespace input get_product_info(product_type::harmonix_rockband_guitar) }; } - case 2: // CELL_PAD_PCLASS_TYPE_DRUM + case CELL_PAD_PCLASS_TYPE_DRUM: { return { @@ -119,25 +254,25 @@ namespace input get_product_info(product_type::rock_revolution_drum_kit) }; } - case 3: // CELL_PAD_PCLASS_TYPE_DJ + case CELL_PAD_PCLASS_TYPE_DJ: { return { get_product_info(product_type::dj_hero_turntable) }; } - case 4: // CELL_PAD_PCLASS_TYPE_DANCEMAT + case CELL_PAD_PCLASS_TYPE_DANCEMAT: { return { get_product_info(product_type::dance_dance_revolution_mat) }; } - case 5: // CELL_PAD_PCLASS_TYPE_NAVIGATION + case CELL_PAD_PCLASS_TYPE_NAVIGATION: { return { - get_product_info(product_type::playstation_3_controller) + get_product_info(product_type::ps_move_navigation) }; } } diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.cpp b/rpcs3/rpcs3qt/pad_settings_dialog.cpp index 761f32d94d..717ca4c8b6 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/pad_settings_dialog.cpp @@ -1612,7 +1612,7 @@ void pad_settings_dialog::HandleDeviceClassChange(int index) const ui->chooseProduct->clear(); - for (const auto& product : input::get_products_by_class(index)) + for (const input::product_info& product : input::get_products_by_class(index)) { switch (product.type) { @@ -1661,6 +1661,11 @@ void pad_settings_dialog::HandleDeviceClassChange(int index) const ui->chooseProduct->addItem(tr("Rock Revolution", "Rock Revolution Drum Controller"), static_cast(product.type)); break; } + case input::product_type::ps_move_navigation: + { + ui->chooseProduct->addItem(tr("PS Move Navigation", "PS Move Navigation Controller"), static_cast(product.type)); + break; + } } } } From 36dce454dddb18e70fd4001c8cf307a4ae5ab74a Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 21 Aug 2023 21:28:37 +0200 Subject: [PATCH 107/184] input: fix dance mat PID It seems this ID was wrong for some reason. --- rpcs3/Input/product_info.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/Input/product_info.h b/rpcs3/Input/product_info.h index 86ce0a09e4..9fb337fa27 100644 --- a/rpcs3/Input/product_info.h +++ b/rpcs3/Input/product_info.h @@ -30,7 +30,6 @@ namespace input { red_octane_gh_guitar = 0x0100, // RedOctane Guitar (Guitar Hero 4 Guitar Controller) red_octane_gh_drum_kit = 0x0120, // RedOctane Drum Kit (Guitar Hero 4 Drum Controller) - dance_dance_revolution_mat = 0x0140, // Dance Dance Revolution Dance Mat Controller dj_hero_turntable = 0x0140, // DJ Hero Turntable Controller harmonix_rockband_guitar = 0x0200, // Harmonix Guitar (Rock Band II Guitar Controller) harmonix_rockband_drum_kit = 0x0210, // Harmonix Drum Kit (Rock Band II Drum Controller) @@ -38,6 +37,7 @@ namespace input playstation_3_controller = 0x0268, // PlayStation 3 Controller rock_revolution_drum_kit = 0x0300, // Rock Revolution Drum Controller ps_move_navigation = 0x042F, // PlayStation Move navigation controller + dance_dance_revolution_mat = 0x1010, // Dance Dance Revolution Dance Mat Controller }; struct product_info From a001e6ef09320f340ab8c9c5d87f176b288262b4 Mon Sep 17 00:00:00 2001 From: Eladash Date: Mon, 21 Aug 2023 19:14:21 +0300 Subject: [PATCH 108/184] Progress Dialog: Fix race on PPU compilation status --- rpcs3/Emu/Cell/PPUThread.cpp | 6 +-- rpcs3/Emu/Cell/SPURecompiler.cpp | 2 +- rpcs3/Emu/System.cpp | 2 +- rpcs3/Emu/system_progress.cpp | 10 ++--- rpcs3/Emu/system_progress.hpp | 67 ++++++++++++++++++++++++++++++-- rpcs3/rpcs3qt/gs_frame.cpp | 2 +- 6 files changed, 74 insertions(+), 15 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index b0aac9b280..32290dbe26 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -3971,7 +3971,7 @@ extern void ppu_initialize() // Validate analyser results (not required) _main.validate(0); - g_progr = "Scanning PPU Modules..."; + progr = "Scanning PPU Modules..."; bool compile_main = false; @@ -4556,7 +4556,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only) if (!workload.empty()) { - g_progr = "Compiling PPU modules..."; + *progr = "Compiling PPU modules..."; } // Create worker threads for compilation (TODO: how many threads) @@ -4624,7 +4624,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only) if (workload.size() < link_workload.size()) { // Only show this message if this task is relevant - g_progr = "Linking PPU modules..."; + *progr = "Linking PPU modules..."; } for (auto [obj_name, is_compiled] : link_workload) diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index dc5ddc657d..952fee9857 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -654,7 +654,7 @@ void spu_cache::initialize() break; } - g_progr_ptotal.wait(v); + thread_ctrl::wait_on(g_progr_ptotal, v); } g_progr_ptotal += ::size32(func_list); diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index f2c390be7c..e85cc6a6cf 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -2678,7 +2678,7 @@ void Emulator::Kill(bool allow_autoexit, bool savestate) { // Show visual feedback to the user in case that stopping takes a while. // This needs to be done before actually stopping, because otherwise the necessary threads will be terminated before we can show an image. - if (auto progress_dialog = g_fxo->try_get>(); progress_dialog && +g_progr) + if (auto progress_dialog = g_fxo->try_get>(); progress_dialog && g_progr.load()) { // We are currently showing a progress dialog. Notify it that we are going to stop emulation. g_system_progress_stopping = true; diff --git a/rpcs3/Emu/system_progress.cpp b/rpcs3/Emu/system_progress.cpp index 3869262349..522dbc79fe 100644 --- a/rpcs3/Emu/system_progress.cpp +++ b/rpcs3/Emu/system_progress.cpp @@ -11,7 +11,7 @@ LOG_CHANNEL(sys_log, "SYS"); // Progress display server synchronization variables -atomic_t g_progr{nullptr}; +atomic_t g_progr{}; atomic_t g_progr_ftotal{0}; atomic_t g_progr_fdone{0}; atomic_t g_progr_ptotal{0}; @@ -40,7 +40,7 @@ void progress_dialog_server::operator()() while (!g_system_progress_stopping && thread_ctrl::state() != thread_state::aborting) { // Wait for the start condition - auto text0 = +g_progr; + const char* text0 = g_progr.load(); while (!text0) { @@ -50,7 +50,7 @@ void progress_dialog_server::operator()() } thread_ctrl::wait_for(5000); - text0 = +g_progr; + text0 = g_progr.load(); } if (g_system_progress_stopping || thread_ctrl::state() == thread_state::aborting) @@ -120,7 +120,7 @@ void progress_dialog_server::operator()() // Update progress while (!g_system_progress_stopping && thread_ctrl::state() != thread_state::aborting) { - const auto text_new = g_progr.load(); + const auto text_new = +g_progr.load(); const u32 ftotal_new = g_progr_ftotal; const u32 fdone_new = g_progr_fdone; @@ -239,5 +239,5 @@ progress_dialog_server::~progress_dialog_server() g_progr_fdone.release(0); g_progr_ptotal.release(0); g_progr_pdone.release(0); - g_progr.release(nullptr); + g_progr.release(progress_dialog_string_t{}); } diff --git a/rpcs3/Emu/system_progress.hpp b/rpcs3/Emu/system_progress.hpp index 92139b4afe..76e8e38ca7 100644 --- a/rpcs3/Emu/system_progress.hpp +++ b/rpcs3/Emu/system_progress.hpp @@ -3,7 +3,19 @@ #include "util/types.hpp" #include "util/atomic.hpp" -extern atomic_t g_progr; +struct alignas(16) progress_dialog_string_t +{ + const char* m_text; + u32 m_user_count; + u32 m_update_id; + + operator const char*() const noexcept + { + return m_text; + } +}; + +extern atomic_t g_progr; extern atomic_t g_progr_ftotal; extern atomic_t g_progr_fdone; extern atomic_t g_progr_ptotal; @@ -15,21 +27,68 @@ extern atomic_t g_system_progress_stopping; class scoped_progress_dialog final { // Saved previous value - const char* const m_prev; + const char* m_prev; + u32 m_prev_id; + u32 m_id; public: scoped_progress_dialog(const char* text) noexcept - : m_prev(g_progr.exchange(text ? text : "")) { + std::tie(m_prev, m_prev_id, m_id) = g_progr.atomic_op([this, text = ensure(text)](progress_dialog_string_t& progr) + { + const char* old = progr.m_text; + progr.m_user_count++; + progr.m_update_id++; + progr.m_text = text; + + ensure(progr.m_user_count > 1 || !old); // Ensure it was nullptr before first use + return std::make_tuple(old, progr.m_update_id - 1, progr.m_update_id); + }); } scoped_progress_dialog(const scoped_progress_dialog&) = delete; scoped_progress_dialog& operator=(const scoped_progress_dialog&) = delete; + scoped_progress_dialog& operator=(const char* text) noexcept + { + // This method is destroying the previous value and replacing it with a new one + std::tie(m_prev, m_prev_id, m_id) = g_progr.atomic_op([this, text = ensure(text)](progress_dialog_string_t& progr) + { + if (m_id == progr.m_update_id) + { + progr.m_update_id = m_prev_id; + progr.m_text = m_prev; + } + + const char* old = progr.m_text; + progr.m_text = text; + progr.m_update_id++; + + ensure(progr.m_user_count > 0); + return std::make_tuple(old, progr.m_update_id - 1, progr.m_update_id); + }); + + return *this; + } + ~scoped_progress_dialog() noexcept { - g_progr.release(m_prev); + g_progr.atomic_op([this](progress_dialog_string_t& progr) + { + if (progr.m_user_count-- == 1) + { + // Clean text only on last user + progr.m_text = nullptr; + progr.m_update_id = 0; + } + else if (m_id == progr.m_update_id) + { + // Restore text only if no other updates were made by other threads + progr.m_text = ensure(m_prev); + progr.m_update_id = m_prev_id; + } + }); } }; diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index dfc3d6917b..c0c4b60a6d 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -579,7 +579,7 @@ bool gs_frame::get_mouse_lock_state() void gs_frame::hide_on_close() { - if (!(+g_progr)) + if (!g_progr.load()) { // Hide the dialog before stopping if no progress bar is being shown. // Otherwise users might think that the game softlocked if stopping takes too long. From bf93f9f9874df43c165c02a79b862c88793b0c84 Mon Sep 17 00:00:00 2001 From: Eladash Date: Tue, 22 Aug 2023 07:18:44 +0300 Subject: [PATCH 109/184] Progress Dialog: Fix race that could lead to ever-inaccurate results --- rpcs3/Emu/system_progress.cpp | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/rpcs3/Emu/system_progress.cpp b/rpcs3/Emu/system_progress.cpp index 522dbc79fe..9c868b497b 100644 --- a/rpcs3/Emu/system_progress.cpp +++ b/rpcs3/Emu/system_progress.cpp @@ -120,12 +120,22 @@ void progress_dialog_server::operator()() // Update progress while (!g_system_progress_stopping && thread_ctrl::state() != thread_state::aborting) { - const auto text_new = +g_progr.load(); + auto whole_state = std::make_tuple(+g_progr.load(), +g_progr_ftotal, +g_progr_fdone, +g_progr_ptotal, +g_progr_pdone); - const u32 ftotal_new = g_progr_ftotal; - const u32 fdone_new = g_progr_fdone; - const u32 ptotal_new = g_progr_ptotal; - const u32 pdone_new = g_progr_pdone; + while (true) + { + const auto new_state = std::make_tuple(+g_progr.load(), +g_progr_ftotal, +g_progr_fdone, +g_progr_ptotal, +g_progr_pdone); + + if (new_state == whole_state) + { + // Only leave while it has a complete (atomic) state + break; + } + + whole_state = new_state; + } + + const auto [text_new, ftotal_new, fdone_new, ptotal_new, pdone_new] = whole_state; if (ftotal != ftotal_new || fdone != fdone_new || ptotal != ptotal_new || pdone != pdone_new || text_new != text1) { @@ -137,8 +147,8 @@ void progress_dialog_server::operator()() if (!text_new) { - // Close dialog - break; + // Incomplete state + continue; } if (show_overlay_message) @@ -182,6 +192,12 @@ void progress_dialog_server::operator()() }); } } + // Leave only if total count is equal to done count + else if (ftotal == fdone && ptotal == pdone && !text_new) + { + // Complete state, empty message: close dialog + break; + } if (show_overlay_message) { From ee3c7f335fb690a40e1e152026e743921d9e80ac Mon Sep 17 00:00:00 2001 From: Eladash Date: Tue, 22 Aug 2023 08:24:44 +0300 Subject: [PATCH 110/184] Progress Dialog: Avoid PPU compilation pop-up on short linkage --- rpcs3/Emu/system_progress.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/rpcs3/Emu/system_progress.cpp b/rpcs3/Emu/system_progress.cpp index 9c868b497b..858331e19f 100644 --- a/rpcs3/Emu/system_progress.cpp +++ b/rpcs3/Emu/system_progress.cpp @@ -117,6 +117,8 @@ void progress_dialog_server::operator()() u32 pdone = 0; auto text1 = text0; + const u64 start_time = get_system_time(); + // Update progress while (!g_system_progress_stopping && thread_ctrl::state() != thread_state::aborting) { @@ -153,11 +155,14 @@ void progress_dialog_server::operator()() if (show_overlay_message) { - // Show a message instead - if (g_cfg.misc.show_ppu_compilation_hint) + // Show a message instead (if compilation period is estimated to be lengthy) + const u64 passed = (get_system_time() - start_time); + + if (pdone < ptotal && g_cfg.misc.show_ppu_compilation_hint && (pdone ? (passed * (ptotal - pdone) / pdone) : (passed * (ptotal + 1))) >= 100'000) { rsx::overlays::show_ppu_compile_notification(); } + thread_ctrl::wait_for(10000); continue; } From a26b8dff185a3f1b0456620609caebca573e29a2 Mon Sep 17 00:00:00 2001 From: Eladash Date: Fri, 18 Aug 2023 20:40:45 +0300 Subject: [PATCH 111/184] rsx: Fix index vertex array range with modulo calculation --- rpcs3/Emu/RSX/Core/RSXVertexTypes.h | 4 +- rpcs3/Emu/RSX/RSXThread.cpp | 103 ++++++++++++++++++++++++++-- 2 files changed, 102 insertions(+), 5 deletions(-) diff --git a/rpcs3/Emu/RSX/Core/RSXVertexTypes.h b/rpcs3/Emu/RSX/Core/RSXVertexTypes.h index e65c168a09..21f1770487 100644 --- a/rpcs3/Emu/RSX/Core/RSXVertexTypes.h +++ b/rpcs3/Emu/RSX/Core/RSXVertexTypes.h @@ -62,11 +62,12 @@ namespace rsx u32 real_offset_address = 0; u8 memory_location = 0; u8 attribute_stride = 0; + std::pair vertex_range{}; rsx::simple_array locations; // Check if we need to upload a full unoptimized range, i.e [0-max_index] - std::pair calculate_required_range(u32 first, u32 count) const; + std::pair calculate_required_range(u32 first, u32 count); }; enum attribute_buffer_placement : u8 @@ -100,6 +101,7 @@ namespace rsx result->single_vertex = false; result->locations.clear(); result->interleaved = true; + result->vertex_range.second = 0; return result; } diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index d480b93387..eeef1124ac 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -269,8 +269,14 @@ namespace rsx } } - std::pair interleaved_range_info::calculate_required_range(u32 first, u32 count) const + std::pair interleaved_range_info::calculate_required_range(u32 first, u32 count) { + if (vertex_range.second) + { + // Cached result + return vertex_range; + } + if (single_vertex) { return { 0, 1 }; @@ -280,10 +286,15 @@ namespace rsx u32 _max_index = 0; u32 _min_index = first; + u32 frequencies[rsx::limits::vertex_count]; + u32 freq_count = rsx::method_registers.current_draw_clause.command == rsx::draw_command::indexed ? 0 : u32{umax}; + u32 max_result_by_division = 0; // Guaranteed maximum + for (const auto &attrib : locations) { if (attrib.frequency <= 1) [[likely]] { + freq_count = umax; _max_index = max_index; } else @@ -294,12 +305,24 @@ namespace rsx { // Actually uses the modulo operator _min_index = 0; - _max_index = attrib.frequency - 1; + _max_index = std::max(_max_index, attrib.frequency - 1); + + if (max_result_by_division < _max_index) + { + if (freq_count != umax) + { + if (std::find(frequencies, frequencies + freq_count, attrib.frequency) == frequencies + freq_count) + { + frequencies[freq_count++] = attrib.frequency; + } + } + } } else { // Same as having no modulo _max_index = max_index; + freq_count = umax; } } else @@ -307,12 +330,84 @@ namespace rsx // Division operator _min_index = std::min(_min_index, first / attrib.frequency); _max_index = std::max(_max_index, utils::aligned_div(max_index, attrib.frequency)); + + if (freq_count > 0 && freq_count != umax) + { + const u32 max = utils::aligned_div(max_index, attrib.frequency); + max_result_by_division = std::max(max_result_by_division, max); + + // Discard lower frequencies because it has been proven that there are indices higher than them + freq_count -= frequencies + freq_count - std::remove_if(frequencies, frequencies + freq_count, [&max_result_by_division](u32 freq) + { + return freq <= max_result_by_division; + }); + } } } } + while (freq_count > 0 && freq_count != umax) + { + const rsx::index_array_type index_type = rsx::method_registers.current_draw_clause.is_immediate_draw ? + rsx::index_array_type::u32 : + rsx::method_registers.index_type(); + + const u32 index_size = index_type == rsx::index_array_type::u32 ? 4 : 2; + + // If we can access a bit a more memory than required - do it + // The alternative would be re-iterating again over all of them + if (get_location(real_offset_address) == CELL_GCM_LOCATION_LOCAL) + { + const auto render = rsx::get_current_renderer(); + + if (utils::add_saturate(real_offset_address - rsx::constants::local_mem_base, (_max_index + 1) * attribute_stride) <= render->local_mem_size) + { + break; + } + } + else if (real_offset_address % 0x100000 + (_max_index + 1) * attribute_stride <= 0x100000)//(vm::check_addr(real_offset_address, vm::page_readable, (_max_index + 1) * attribute_stride)) + { + break; + } + + _max_index = 0; + + // Force aligned indices as realhw + const u32 address = (0 - index_size) & get_address(rsx::method_registers.index_array_address(), rsx::method_registers.index_array_location()); + + auto re_evaluate = [&](auto ptr) + { + for (u32 _index = first; _index < first + count; _index++) + { + const auto value = ptr[_index]; + + for (u32 freq_it = 0; freq_it < freq_count; freq_it++) + { + const auto res = value % frequencies[freq_it]; + + if (res > _max_index) + { + _max_index = value; + } + } + } + }; + + if (index_size == 4) + { + re_evaluate(vm::get_super_ptr(address)); + } + else + { + re_evaluate(vm::get_super_ptr(address)); + } + + break; + } + ensure(_max_index >= _min_index); - return { _min_index, (_max_index - _min_index) + 1 }; + vertex_range = { _min_index, (_max_index - _min_index) + 1 }; + return vertex_range; } u32 get_vertex_type_size_on_host(vertex_base_type type, u32 size) @@ -2798,7 +2893,7 @@ namespace rsx if (persistent != nullptr) { - for (const auto &block : layout.interleaved_blocks) + for (interleaved_range_info* block : layout.interleaved_blocks) { auto range = block->calculate_required_range(first_vertex, vertex_count); From 2022098b13cca8bd7837e49ccb8f0cc3eebfb998 Mon Sep 17 00:00:00 2001 From: Eladash Date: Tue, 22 Aug 2023 10:25:06 +0300 Subject: [PATCH 112/184] Progress Dialog: Fix race when PPU compilation is super fast --- rpcs3/Emu/system_progress.cpp | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/rpcs3/Emu/system_progress.cpp b/rpcs3/Emu/system_progress.cpp index 858331e19f..229fae9197 100644 --- a/rpcs3/Emu/system_progress.cpp +++ b/rpcs3/Emu/system_progress.cpp @@ -49,6 +49,41 @@ void progress_dialog_server::operator()() break; } + if (g_progr_ftotal || g_progr_fdone || g_progr_ptotal || g_progr_pdone) + { + auto whole_state = std::make_tuple(+g_progr.load(), +g_progr_ftotal, +g_progr_fdone, +g_progr_ptotal, +g_progr_pdone); + + while (true) + { + const auto new_state = std::make_tuple(+g_progr.load(), +g_progr_ftotal, +g_progr_fdone, +g_progr_ptotal, +g_progr_pdone); + + if (new_state == whole_state) + { + // Only leave while it has a complete (atomic) state + break; + } + + whole_state = new_state; + } + + const auto [text_new, ftotal, fdone, ptotal, pdone] = whole_state; + + if (text_new) + { + text0 = text_new; + break; + } + else if ((ftotal || ptotal) && ftotal == fdone && ptotal == pdone) + { + // Cleanup (missed message but do not cry over spilt milk) + g_progr_fdone -= fdone; + g_progr_pdone -= pdone; + g_progr_ftotal -= ftotal; + g_progr_ptotal -= ptotal; + g_progr_ptotal.notify_all(); + } + } + thread_ctrl::wait_for(5000); text0 = g_progr.load(); } From e2d4d400ff3bc757e4584792b46cd1e8f364faec Mon Sep 17 00:00:00 2001 From: Eladash Date: Tue, 22 Aug 2023 11:13:13 +0300 Subject: [PATCH 113/184] rsx: Fixup calculate_required_range --- rpcs3/Emu/RSX/GL/GLDraw.cpp | 9 ++++++++ rpcs3/Emu/RSX/RSXThread.cpp | 42 ++++++++++++++++++++++++++++--------- rpcs3/Emu/RSX/RSXThread.h | 2 +- rpcs3/Emu/RSX/VK/VKDraw.cpp | 9 ++++++++ 4 files changed, 51 insertions(+), 11 deletions(-) diff --git a/rpcs3/Emu/RSX/GL/GLDraw.cpp b/rpcs3/Emu/RSX/GL/GLDraw.cpp index 6eba64a9d6..3a8e47f4a7 100644 --- a/rpcs3/Emu/RSX/GL/GLDraw.cpp +++ b/rpcs3/Emu/RSX/GL/GLDraw.cpp @@ -514,10 +514,19 @@ void GLGSRender::emit_geometry(u32 sub_index) // Rebase vertex bases instead of for (auto& info : m_vertex_layout.interleaved_blocks) { + info->vertex_range.second = 0; const auto vertex_base_offset = rsx::method_registers.vertex_data_base_offset(); info->real_offset_address = rsx::get_address(rsx::get_vertex_offset_from_base(vertex_base_offset, info->base_offset), info->memory_location); } } + else + { + // Discard cached results + for (auto& info : m_vertex_layout.interleaved_blocks) + { + info->vertex_range.second = 0; + } + } if (vertex_state && !m_vertex_layout.validate()) { diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index eeef1124ac..8b1490b7fa 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -354,12 +354,12 @@ namespace rsx const u32 index_size = index_type == rsx::index_array_type::u32 ? 4 : 2; + const auto render = rsx::get_current_renderer(); + // If we can access a bit a more memory than required - do it // The alternative would be re-iterating again over all of them if (get_location(real_offset_address) == CELL_GCM_LOCATION_LOCAL) { - const auto render = rsx::get_current_renderer(); - if (utils::add_saturate(real_offset_address - rsx::constants::local_mem_base, (_max_index + 1) * attribute_stride) <= render->local_mem_size) { break; @@ -372,14 +372,18 @@ namespace rsx _max_index = 0; - // Force aligned indices as realhw - const u32 address = (0 - index_size) & get_address(rsx::method_registers.index_array_address(), rsx::method_registers.index_array_location()); - - auto re_evaluate = [&](auto ptr) + auto re_evaluate = [&] (const std::byte* ptr, T) { + const u64 restart = rsx::method_registers.restart_index_enabled() ? rsx::method_registers.restart_index() : u64{umax}; + for (u32 _index = first; _index < first + count; _index++) { - const auto value = ptr[_index]; + const auto value = read_from_ptr>(ptr, _index * sizeof(T)); + + if (value == restart) + { + continue; + } for (u32 freq_it = 0; freq_it < freq_count; freq_it++) { @@ -387,7 +391,7 @@ namespace rsx if (res > _max_index) { - _max_index = value; + _max_index = res; } } } @@ -395,11 +399,29 @@ namespace rsx if (index_size == 4) { - re_evaluate(vm::get_super_ptr(address)); + if (!render->element_push_buffer.empty()) [[unlikely]] + { + // Indices provided via immediate mode + re_evaluate(reinterpret_cast(render->element_push_buffer.data()), u32{}); + } + else + { + const u32 address = (0 - index_size) & get_address(rsx::method_registers.index_array_address(), rsx::method_registers.index_array_location()); + re_evaluate(vm::get_super_ptr(address), u32{}); + } } else { - re_evaluate(vm::get_super_ptr(address)); + if (!render->element_push_buffer.empty()) [[unlikely]] + { + // Indices provided via immediate mode + re_evaluate(reinterpret_cast(render->element_push_buffer.data()), u16{}); + } + else + { + const u32 address = (0 - index_size) & get_address(rsx::method_registers.index_array_address(), rsx::method_registers.index_array_location()); + re_evaluate(vm::get_super_ptr(address), u16{}); + } } break; diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index 18306cc4cf..4f378939fd 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -159,7 +159,6 @@ namespace rsx protected: std::array vertex_push_buffers; - rsx::simple_array element_push_buffer; s32 m_skip_frame_ctr = 0; bool skip_current_frame = false; @@ -215,6 +214,7 @@ namespace rsx atomic_t external_interrupt_lock{ 0 }; atomic_t external_interrupt_ack{ false }; atomic_t is_initialized{0}; + rsx::simple_array element_push_buffer; bool is_fifo_idle() const; void flush_fifo(); diff --git a/rpcs3/Emu/RSX/VK/VKDraw.cpp b/rpcs3/Emu/RSX/VK/VKDraw.cpp index 82498ba91a..164e62e300 100644 --- a/rpcs3/Emu/RSX/VK/VKDraw.cpp +++ b/rpcs3/Emu/RSX/VK/VKDraw.cpp @@ -718,10 +718,19 @@ void VKGSRender::emit_geometry(u32 sub_index) // Rebase vertex bases instead of for (auto& info : m_vertex_layout.interleaved_blocks) { + info->vertex_range.second = 0; const auto vertex_base_offset = rsx::method_registers.vertex_data_base_offset(); info->real_offset_address = rsx::get_address(rsx::get_vertex_offset_from_base(vertex_base_offset, info->base_offset), info->memory_location); } } + else + { + // Discard cached results + for (auto& info : m_vertex_layout.interleaved_blocks) + { + info->vertex_range.second = 0; + } + } if (vertex_state && !m_vertex_layout.validate()) { From 756ab1191eaea55b4e507c8a93c83bf2b4042f20 Mon Sep 17 00:00:00 2001 From: Eladash Date: Wed, 23 Aug 2023 08:19:49 +0300 Subject: [PATCH 114/184] PPU Analyzer: Fix for unaligned sections --- rpcs3/Emu/Cell/PPUAnalyser.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/rpcs3/Emu/Cell/PPUAnalyser.cpp b/rpcs3/Emu/Cell/PPUAnalyser.cpp index 2a774d0fd3..79ff56fb62 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.cpp +++ b/rpcs3/Emu/Cell/PPUAnalyser.cpp @@ -699,6 +699,11 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b // Find OPD section for (const auto& sec : secs) { + if (sec.size % 8) + { + continue; + } + vm::cptr sec_end = vm::cast(sec.addr + sec.size); // Probe @@ -785,6 +790,11 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b // Find .eh_frame section for (const auto& sec : secs) { + if (sec.size % 4) + { + continue; + } + vm::cptr sec_end = vm::cast(sec.addr + sec.size); // Probe From 4462b7be5cbe4f15f861ff9d322154d786f901e0 Mon Sep 17 00:00:00 2001 From: Eladash Date: Wed, 23 Aug 2023 08:45:13 +0300 Subject: [PATCH 115/184] cellGame: Make cellGameDataCheck slower * Turns out cellGameBootCheck is actually quite fast. * cellGameDataCheck is incredibly slow, slower for DISC type. * Set 0 sizeKB for when RET_NONE is about to be returned. --- rpcs3/Emu/Cell/Modules/cellGame.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellGame.cpp b/rpcs3/Emu/Cell/Modules/cellGame.cpp index 27766431d3..d34539c2f0 100644 --- a/rpcs3/Emu/Cell/Modules/cellGame.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGame.cpp @@ -703,7 +703,7 @@ error_code cellGameBootCheck(vm::ptr type, vm::ptr attributes, vm::ptr auto& perm = g_fxo->get(); - lv2_sleep(5000); + lv2_sleep(500); const auto init = perm.init.init(); @@ -831,8 +831,6 @@ error_code cellGameDataCheck(u32 type, vm::cptr dirName, vm::ptrget(); @@ -841,9 +839,14 @@ error_code cellGameDataCheck(u32 type, vm::cptr dirName, vm::ptr dirName, vm::ptrhddFreeSizeKB = 40 * 1024 * 1024 - 1; // Read explanation in cellHddGameCheck // TODO: Calculate data size for game data, if necessary. - size->sizeKB = CELL_GAME_SIZEKB_NOTCALC; + size->sizeKB = sfo.empty() ? 0 : CELL_GAME_SIZEKB_NOTCALC; size->sysSizeKB = 0; // TODO } From 4794869bd8a2ff9857482ce8b56428375772bd09 Mon Sep 17 00:00:00 2001 From: Eladash Date: Wed, 23 Aug 2023 09:21:02 +0300 Subject: [PATCH 116/184] sceNpDrm: Slow down sceNpDrmIsAvailable --- rpcs3/Emu/Cell/Modules/sceNp.cpp | 33 ++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/sceNp.cpp b/rpcs3/Emu/Cell/Modules/sceNp.cpp index da84bb05a5..a4ef65114d 100644 --- a/rpcs3/Emu/Cell/Modules/sceNp.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNp.cpp @@ -17,6 +17,7 @@ #include "Emu/Cell/lv2/sys_time.h" #include "Emu/Cell/lv2/sys_fs.h" +#include "Emu/Cell/lv2/sys_sync.h" #include "Emu/NP/np_handler.h" #include "Emu/NP/np_contexts.h" #include "Emu/NP/np_helpers.h" @@ -408,6 +409,8 @@ void message_data::print() const sceNp.notice("commId: %s, msgId: %d, mainType: %d, subType: %d, subject: %s, body: %s, data_size: %d", static_cast(commId.data), msgId, mainType, subType, subject, body, data.size()); } +extern void lv2_sleep(u64 timeout, ppu_thread* ppu = nullptr); + error_code sceNpInit(u32 poolsize, vm::ptr poolptr) { sceNp.warning("sceNpInit(poolsize=0x%x, poolptr=*0x%x)", poolsize, poolptr); @@ -553,18 +556,40 @@ error_code npDrmIsAvailable(vm::cptr k_licensee_addr, vm::cptr drm_pat return CELL_OK; } -error_code sceNpDrmIsAvailable(vm::cptr k_licensee_addr, vm::cptr drm_path) +error_code sceNpDrmIsAvailable(ppu_thread& ppu, vm::cptr k_licensee_addr, vm::cptr drm_path) { sceNp.warning("sceNpDrmIsAvailable(k_licensee=*0x%x, drm_path=*0x%x)", k_licensee_addr, drm_path); - return npDrmIsAvailable(k_licensee_addr, drm_path); + if (!drm_path) + { + return SCE_NP_DRM_ERROR_INVALID_PARAM; + } + + lv2_obj::sleep(ppu); + + const auto ret = npDrmIsAvailable(k_licensee_addr, drm_path); + lv2_sleep(100000, &ppu); + + return ret; } -error_code sceNpDrmIsAvailable2(vm::cptr k_licensee_addr, vm::cptr drm_path) +error_code sceNpDrmIsAvailable2(ppu_thread& ppu, vm::cptr k_licensee_addr, vm::cptr drm_path) { sceNp.warning("sceNpDrmIsAvailable2(k_licensee=*0x%x, drm_path=*0x%x)", k_licensee_addr, drm_path); - return npDrmIsAvailable(k_licensee_addr, drm_path); + if (!drm_path) + { + return SCE_NP_DRM_ERROR_INVALID_PARAM; + } + + lv2_obj::sleep(ppu); + + const auto ret = npDrmIsAvailable(k_licensee_addr, drm_path); + + // TODO: Accurate sleep time + //lv2_sleep(20000, &ppu); + + return ret; } error_code npDrmVerifyUpgradeLicense(vm::cptr content_id) From d8af3ea8557e2c672fd4da50865c619cc3b3e6e9 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 22 Aug 2023 23:31:08 +0200 Subject: [PATCH 117/184] overlays: fix some warnings, simplify code, use move and references --- rpcs3/Emu/Cell/PPUModule.cpp | 2 +- rpcs3/Emu/Cell/PPUThread.cpp | 6 +- .../Shaders/shader_loading_dialog.cpp | 6 +- .../Overlays/Shaders/shader_loading_dialog.h | 2 +- .../Shaders/shader_loading_dialog_native.cpp | 4 +- .../Shaders/shader_loading_dialog_native.h | 2 +- .../RSX/Overlays/overlay_message_dialog.cpp | 8 +-- .../Emu/RSX/Overlays/overlay_message_dialog.h | 4 +- rpcs3/Emu/RSX/Overlays/overlay_utils.cpp | 8 +-- rpcs3/Emu/RSX/Overlays/overlays.cpp | 4 +- rpcs3/Emu/RSX/Overlays/overlays.h | 6 +- rpcs3/Emu/system_progress.cpp | 72 +++++++++---------- rpcs3/rpcs3qt/msg_dialog_frame.cpp | 12 ++-- 13 files changed, 65 insertions(+), 71 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index 64e197e3e3..4df1749bcd 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -2494,7 +2494,7 @@ std::pair, CellError> ppu_load_overlay(const ppu_ex } } - const auto ovlm = std::make_shared(); + std::shared_ptr ovlm = std::make_shared(); // Set path (TODO) ovlm->name = path.substr(path.find_last_of('/') + 1); diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 32290dbe26..49aa70e8d0 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -3751,7 +3751,7 @@ extern void ppu_precompile(std::vector& dir_queue, std::vector& dir_queue, std::vectorstate.all_of(cpu_flag::exit) : Emu.IsStopped()) { diff --git a/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog.cpp b/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog.cpp index 4d20bddc9d..d6a9f74f6b 100644 --- a/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog.cpp +++ b/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog.cpp @@ -40,7 +40,7 @@ namespace rsx } } - void shader_loading_dialog::update_msg(u32 index, const std::string& msg) + void shader_loading_dialog::update_msg(u32 index, std::string msg) { if (!dlg) { @@ -49,9 +49,9 @@ namespace rsx ref_cnt++; - Emu.CallFromMainThread([&, index, msg]() + Emu.CallFromMainThread([&, index, message = std::move(msg)]() { - dlg->ProgressBarSetMsg(index, msg); + dlg->ProgressBarSetMsg(index, message); ref_cnt--; }); } diff --git a/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog.h b/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog.h index 7d141c49d2..ae95b4bda8 100644 --- a/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog.h +++ b/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog.h @@ -11,7 +11,7 @@ namespace rsx virtual ~shader_loading_dialog() = default; virtual void create(const std::string& msg, const std::string& title); - virtual void update_msg(u32 index, const std::string& msg); + virtual void update_msg(u32 index, std::string msg); virtual void inc_value(u32 index, u32 value); virtual void set_value(u32 index, u32 value); virtual void set_limit(u32 index, u32 limit); diff --git a/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog_native.cpp b/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog_native.cpp index 7b8ddd29c8..c0d0f4ff93 100644 --- a/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog_native.cpp +++ b/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog_native.cpp @@ -31,9 +31,9 @@ namespace rsx }); } - void shader_loading_dialog_native::update_msg(u32 index, const std::string& msg) + void shader_loading_dialog_native::update_msg(u32 index, std::string msg) { - dlg->progress_bar_set_message(index, msg); + dlg->progress_bar_set_message(index, std::move(msg)); owner->flip({}); } diff --git a/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog_native.h b/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog_native.h index 08ad43bfcf..0d73324562 100644 --- a/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog_native.h +++ b/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog_native.h @@ -21,7 +21,7 @@ namespace rsx shader_loading_dialog_native(GSRender* ptr); void create(const std::string& msg, const std::string&/* title*/) override; - void update_msg(u32 index, const std::string& msg) override; + void update_msg(u32 index, std::string msg) override; void inc_value(u32 index, u32 value) override; void set_value(u32 index, u32 value) override; void set_limit(u32 index, u32 limit) override; diff --git a/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.cpp b/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.cpp index 078ccb9c85..ac8c359bdd 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.cpp @@ -342,9 +342,9 @@ namespace rsx return CELL_OK; } - void message_dialog::set_text(const std::string& text) + void message_dialog::set_text(std::string text) { - text_guard.set_text(text); + text_guard.set_text(std::move(text)); } void message_dialog::update_custom_background() @@ -411,12 +411,12 @@ namespace rsx taskbar_index = index; } - error_code message_dialog::progress_bar_set_message(u32 index, const std::string& msg) + error_code message_dialog::progress_bar_set_message(u32 index, std::string msg) { if (index >= num_progress_bars) return CELL_MSGDIALOG_ERROR_PARAM; - ::at32(bar_text_guard, index).set_text(msg); + ::at32(bar_text_guard, index).set_text(std::move(msg)); return CELL_OK; } diff --git a/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.h b/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.h index 989dec8c16..cfc5d44eba 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.h @@ -72,12 +72,12 @@ namespace rsx error_code show(bool is_blocking, const std::string& text, const MsgDialogType& type, std::function on_close); - void set_text(const std::string& text); + void set_text(std::string text); void update_custom_background(); u32 progress_bar_count() const; void progress_bar_set_taskbar_index(s32 index); - error_code progress_bar_set_message(u32 index, const std::string& msg); + error_code progress_bar_set_message(u32 index, std::string msg); error_code progress_bar_increment(u32 index, f32 value); error_code progress_bar_set_value(u32 index, f32 value); error_code progress_bar_reset(u32 index); diff --git a/rpcs3/Emu/RSX/Overlays/overlay_utils.cpp b/rpcs3/Emu/RSX/Overlays/overlay_utils.cpp index 4be5d07c79..65dde9a8a7 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_utils.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_utils.cpp @@ -60,10 +60,10 @@ static auto s_ascii_lowering_map = []() }(); template -void process_multibyte(const std::string s, F&& func) +void process_multibyte(const std::string& s, F&& func) { - const auto end = s.length(); - for (u32 index = 0; index < end; ++index) + const usz end = s.length(); + for (usz index = 0; index < end; ++index) { const u8 code = static_cast(s[index]); @@ -78,7 +78,7 @@ void process_multibyte(const std::string s, F&& func) continue; } - const auto extra_bytes = (code <= 0xDF) ? 1u : (code <= 0xEF) ? 2u : 3u; + const u32 extra_bytes = (code <= 0xDF) ? 1u : (code <= 0xEF) ? 2u : 3u; if ((index + extra_bytes) > end) { // Malformed string, abort diff --git a/rpcs3/Emu/RSX/Overlays/overlays.cpp b/rpcs3/Emu/RSX/Overlays/overlays.cpp index e003b7fd5a..a8064a8803 100644 --- a/rpcs3/Emu/RSX/Overlays/overlays.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlays.cpp @@ -18,7 +18,7 @@ namespace rsx { thread_local DECLARE(user_interface::g_thread_bit) = 0; - u64 user_interface::alloc_thread_bit() + u32 user_interface::alloc_thread_bit() { auto [_old, ok] = this->thread_bits.fetch_op([](u32& bits) { @@ -38,7 +38,7 @@ namespace rsx return 0; } - const u64 r = u64{1} << std::countr_one(_old); + const u32 r = u32{1} << std::countr_one(_old); ::overlays.trace("Bit allocated (%u)", r); return r; } diff --git a/rpcs3/Emu/RSX/Overlays/overlays.h b/rpcs3/Emu/RSX/Overlays/overlays.h index ac23e00406..f12d0ed209 100644 --- a/rpcs3/Emu/RSX/Overlays/overlays.h +++ b/rpcs3/Emu/RSX/Overlays/overlays.h @@ -90,9 +90,9 @@ namespace rsx bool m_keyboard_pad_handler_active = true; // Initialized as true to prevent keyboard input until proven otherwise. bool m_allow_input_on_pause = false; - static thread_local u64 g_thread_bit; + static thread_local u32 g_thread_bit; - u64 alloc_thread_bit(); + u32 alloc_thread_bit(); std::function on_close = nullptr; @@ -114,7 +114,7 @@ namespace rsx private: user_interface* m_parent; - u64 m_thread_bit; + u32 m_thread_bit; }; public: s32 return_code = 0; // CELL_OK diff --git a/rpcs3/Emu/system_progress.cpp b/rpcs3/Emu/system_progress.cpp index 229fae9197..f829944c39 100644 --- a/rpcs3/Emu/system_progress.cpp +++ b/rpcs3/Emu/system_progress.cpp @@ -37,6 +37,26 @@ void progress_dialog_server::operator()() std::shared_ptr native_dlg; g_system_progress_stopping = false; + const auto get_state = []() + { + auto whole_state = std::make_tuple(+g_progr.load(), +g_progr_ftotal, +g_progr_fdone, +g_progr_ptotal, +g_progr_pdone); + + while (true) + { + auto new_state = std::make_tuple(+g_progr.load(), +g_progr_ftotal, +g_progr_fdone, +g_progr_ptotal, +g_progr_pdone); + + if (new_state == whole_state) + { + // Only leave while it has a complete (atomic) state + return whole_state; + } + + whole_state = std::move(new_state); + } + + return whole_state; + }; + while (!g_system_progress_stopping && thread_ctrl::state() != thread_state::aborting) { // Wait for the start condition @@ -51,29 +71,15 @@ void progress_dialog_server::operator()() if (g_progr_ftotal || g_progr_fdone || g_progr_ptotal || g_progr_pdone) { - auto whole_state = std::make_tuple(+g_progr.load(), +g_progr_ftotal, +g_progr_fdone, +g_progr_ptotal, +g_progr_pdone); - - while (true) - { - const auto new_state = std::make_tuple(+g_progr.load(), +g_progr_ftotal, +g_progr_fdone, +g_progr_ptotal, +g_progr_pdone); - - if (new_state == whole_state) - { - // Only leave while it has a complete (atomic) state - break; - } - - whole_state = new_state; - } - - const auto [text_new, ftotal, fdone, ptotal, pdone] = whole_state; + const auto& [text_new, ftotal, fdone, ptotal, pdone] = get_state(); if (text_new) { text0 = text_new; break; } - else if ((ftotal || ptotal) && ftotal == fdone && ptotal == pdone) + + if ((ftotal || ptotal) && ftotal == fdone && ptotal == pdone) { // Cleanup (missed message but do not cry over spilt milk) g_progr_fdone -= fdone; @@ -157,22 +163,7 @@ void progress_dialog_server::operator()() // Update progress while (!g_system_progress_stopping && thread_ctrl::state() != thread_state::aborting) { - auto whole_state = std::make_tuple(+g_progr.load(), +g_progr_ftotal, +g_progr_fdone, +g_progr_ptotal, +g_progr_pdone); - - while (true) - { - const auto new_state = std::make_tuple(+g_progr.load(), +g_progr_ftotal, +g_progr_fdone, +g_progr_ptotal, +g_progr_pdone); - - if (new_state == whole_state) - { - // Only leave while it has a complete (atomic) state - break; - } - - whole_state = new_state; - } - - const auto [text_new, ftotal_new, fdone_new, ptotal_new, pdone_new] = whole_state; + const auto& [text_new, ftotal_new, fdone_new, ptotal_new, pdone_new] = get_state(); if (ftotal != ftotal_new || fdone != fdone_new || ptotal != ptotal_new || pdone != pdone_new || text_new != text1) { @@ -191,11 +182,16 @@ void progress_dialog_server::operator()() if (show_overlay_message) { // Show a message instead (if compilation period is estimated to be lengthy) - const u64 passed = (get_system_time() - start_time); - - if (pdone < ptotal && g_cfg.misc.show_ppu_compilation_hint && (pdone ? (passed * (ptotal - pdone) / pdone) : (passed * (ptotal + 1))) >= 100'000) + if (pdone < ptotal && g_cfg.misc.show_ppu_compilation_hint) { - rsx::overlays::show_ppu_compile_notification(); + const u64 passed_usec = (get_system_time() - start_time); + const u64 remaining_usec = passed_usec * (pdone ? ((static_cast(ptotal) - pdone) / pdone) : ptotal); + + // Only show compile notification if we estimate at least 100ms + if (remaining_usec >= 100'000ULL) + { + rsx::overlays::show_ppu_compile_notification(); + } } thread_ctrl::wait_for(10000); @@ -219,7 +215,7 @@ void progress_dialog_server::operator()() if (native_dlg) { native_dlg->set_text(text_new); - native_dlg->progress_bar_set_message(0, progr); + native_dlg->progress_bar_set_message(0, std::move(progr)); native_dlg->progress_bar_set_value(0, std::floor(value)); } else if (dlg) diff --git a/rpcs3/rpcs3qt/msg_dialog_frame.cpp b/rpcs3/rpcs3qt/msg_dialog_frame.cpp index e05c1f87e0..32bb19c6f8 100644 --- a/rpcs3/rpcs3qt/msg_dialog_frame.cpp +++ b/rpcs3/rpcs3qt/msg_dialog_frame.cpp @@ -5,8 +5,6 @@ #include #include -constexpr auto qstr = QString::fromStdString; - void msg_dialog_frame::Create(const std::string& msg, const std::string& title) { state = MsgDialogState::Open; @@ -16,10 +14,10 @@ void msg_dialog_frame::Create(const std::string& msg, const std::string& title) Close(true); m_dialog = new custom_dialog(type.disable_cancel); - m_dialog->setWindowTitle(title.empty() ? (type.se_normal ? tr("Normal dialog") : tr("Error dialog")) : qstr(title)); + m_dialog->setWindowTitle(title.empty() ? (type.se_normal ? tr("Normal dialog") : tr("Error dialog")) : QString::fromStdString(title)); m_dialog->setWindowOpacity(type.bg_invisible ? 1. : 0.75); - m_text = new QLabel(qstr(msg)); + m_text = new QLabel(QString::fromStdString(msg)); m_text->setAlignment(Qt::AlignCenter); // Layout @@ -159,7 +157,7 @@ void msg_dialog_frame::SetMsg(const std::string& msg) { if (m_dialog) { - m_text->setText(qstr(msg)); + m_text->setText(QString::fromStdString(msg)); } } @@ -171,14 +169,14 @@ void msg_dialog_frame::ProgressBarSetMsg(u32 index, const std::string& msg) { if (m_text1) { - m_text1->setText(qstr(msg)); + m_text1->setText(QString::fromStdString(msg)); } } else if (index == 1) { if (m_text2) { - m_text2->setText(qstr(msg)); + m_text2->setText(QString::fromStdString(msg)); } } } From 06e4b6251c74f29a7aec2a1e0523ec2f1189cb16 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 23 Aug 2023 08:09:09 +0200 Subject: [PATCH 118/184] Fix Vulkan link in README --- BUILDING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILDING.md b/BUILDING.md index 5740a80dd0..e816f4b2ac 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -57,7 +57,7 @@ For Ubuntu systems, it is strongly recommended to use the PPA from [LunarG](http ``` . /etc/os-release wget -qO - https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add - -sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-1.3.224-$UBUNTU_CODENAME.list https://packages.lunarg.com/vulkan/1.3.224/lunarg-vulkan-1.2.198-$UBUNTU_CODENAME.list +sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-1.3.224-$UBUNTU_CODENAME.list https://packages.lunarg.com/vulkan/1.3.224/lunarg-vulkan-1.3.224-$UBUNTU_CODENAME.list sudo apt update sudo apt install vulkan-sdk ``` From 6b7f4cbe1796415a411036fbf3dc6c4f0cf49e8f Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 23 Aug 2023 21:21:11 +0200 Subject: [PATCH 119/184] cellPad: remove unused variables --- rpcs3/Emu/Cell/Modules/cellPad.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellPad.cpp b/rpcs3/Emu/Cell/Modules/cellPad.cpp index 03658b8645..839dbb47d3 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPad.cpp @@ -510,8 +510,6 @@ error_code cellPadPeriphGetInfo(vm::ptr info) info->max_connect = config.max_connect; info->system_info = rinfo.system_info; - const auto& pads = handler->GetPads(); - u32 now_connect = 0; for (u32 i = 0; i < CELL_PAD_MAX_PORT_NUM; ++i) @@ -702,8 +700,6 @@ error_code cellPadGetInfo(vm::ptr info) u32 now_connect = 0; - const auto& pads = handler->GetPads(); - for (u32 i = 0; i < CELL_MAX_PADS; ++i) { if (i >= config.get_max_connect()) From 8772219492118f538e4b6b97e5449c4f3fc66aca Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 23 Aug 2023 21:23:30 +0200 Subject: [PATCH 120/184] Remove unused variable in PPUThread.cpp --- rpcs3/Emu/Cell/PPUThread.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 49aa70e8d0..775695f0a2 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -1738,8 +1738,6 @@ std::vector> ppu_thread::dump_callstack_list() const const auto type = g_ppu_itype.decode(test_op.opcode); - const u32 spr = ((test_op.spr >> 5) | ((test_op.spr & 0x1f) << 5)); - if (type == ppu_itype::BCLR) { break; From 5fde96d56399d1af008b205f7fabbc117ab00012 Mon Sep 17 00:00:00 2001 From: Eladash Date: Thu, 24 Aug 2023 16:27:24 +0300 Subject: [PATCH 121/184] rsx: Discard color mask writes with reserved bits --- rpcs3/Emu/RSX/RSXDisAsm.cpp | 6 +++++- rpcs3/Emu/RSX/rsx_decode.h | 13 ++++++++++++- rpcs3/Emu/RSX/rsx_methods.cpp | 19 ++++++++++++++++++- rpcs3/rpcs3qt/syntax_highlighter.cpp | 2 +- 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/rpcs3/Emu/RSX/RSXDisAsm.cpp b/rpcs3/Emu/RSX/RSXDisAsm.cpp index 5d25284c61..7abcc179fe 100644 --- a/rpcs3/Emu/RSX/RSXDisAsm.cpp +++ b/rpcs3/Emu/RSX/RSXDisAsm.cpp @@ -167,7 +167,11 @@ void RSXDisAsm::Write(std::string_view str, s32 count, bool is_non_inc, u32 id) { last_opcode.clear(); - if (count >= 0) + if (count == 1 && !is_non_inc) + { + fmt::append(last_opcode, "[%08x] ( )", dump_pc); + } + else if (count >= 0) { fmt::append(last_opcode, "[%08x] (%s%u)", dump_pc, is_non_inc ? "+" : "", count); } diff --git a/rpcs3/Emu/RSX/rsx_decode.h b/rpcs3/Emu/RSX/rsx_decode.h index 42a5fb9f11..10b58d59c4 100644 --- a/rpcs3/Emu/RSX/rsx_decode.h +++ b/rpcs3/Emu/RSX/rsx_decode.h @@ -1592,7 +1592,7 @@ struct registers_decoder static void dump(std::string& out, const decoded_type& decoded) { - fmt::append(out, "Surface: Z offset: %u", decoded.surface_z_offset()); + fmt::append(out, "Surface: Z offset: 0x%x", decoded.surface_z_offset()); } }; @@ -3040,10 +3040,21 @@ struct registers_decoder { return value != 0; } + + u32 is_invalid() const + { + return (value & 0xfefefefe) ? value : 0; + } }; static void dump(std::string& out, const decoded_type& decoded) { + if (u32 invalid_value = decoded.is_invalid()) + { + fmt::append(out, "Surface: color mask: invalid = 0x%08x", invalid_value); + return; + } + fmt::append(out, "Surface: color mask A = %s R = %s G = %s B = %s" , print_boolean(decoded.color_a()), print_boolean(decoded.color_r()), print_boolean(decoded.color_g()), print_boolean(decoded.color_b())); } diff --git a/rpcs3/Emu/RSX/rsx_methods.cpp b/rpcs3/Emu/RSX/rsx_methods.cpp index fb78fd790d..6598e69f62 100644 --- a/rpcs3/Emu/RSX/rsx_methods.cpp +++ b/rpcs3/Emu/RSX/rsx_methods.cpp @@ -849,6 +849,23 @@ namespace rsx } } + void set_color_mask(thread* rsx, u32 reg, u32 arg) + { + if (arg == method_registers.register_previous_value) + { + return; + } + + if (method_registers.decode(arg).is_invalid()) [[ unlikely ]] + { + method_registers.decode(reg, method_registers.register_previous_value); + } + else + { + set_surface_options_dirty_bit(rsx, reg, arg); + } + } + void set_stencil_op(thread* rsx, u32 reg, u32 arg) { if (arg == method_registers.register_previous_value) @@ -3550,7 +3567,7 @@ namespace rsx bind(NV4097_SET_DEPTH_TEST_ENABLE, nv4097::set_surface_options_dirty_bit); bind(NV4097_SET_DEPTH_FUNC, nv4097::set_surface_options_dirty_bit); bind(NV4097_SET_DEPTH_MASK, nv4097::set_surface_options_dirty_bit); - bind(NV4097_SET_COLOR_MASK, nv4097::set_surface_options_dirty_bit); + bind(NV4097_SET_COLOR_MASK, nv4097::set_color_mask); bind(NV4097_SET_COLOR_MASK_MRT, nv4097::set_surface_options_dirty_bit); bind(NV4097_SET_TWO_SIDED_STENCIL_TEST_ENABLE, nv4097::set_surface_options_dirty_bit); bind(NV4097_SET_STENCIL_TEST_ENABLE, nv4097::set_surface_options_dirty_bit); diff --git a/rpcs3/rpcs3qt/syntax_highlighter.cpp b/rpcs3/rpcs3qt/syntax_highlighter.cpp index 4d04e66e2b..2f889d3fa4 100644 --- a/rpcs3/rpcs3qt/syntax_highlighter.cpp +++ b/rpcs3/rpcs3qt/syntax_highlighter.cpp @@ -67,7 +67,7 @@ LogHighlighter::LogHighlighter(QTextDocument* parent) : Highlighter(parent) AsmHighlighter::AsmHighlighter(QTextDocument *parent) : Highlighter(parent) { - addRule("^\b[A-Z0-9]+\b", Qt::darkBlue); // Instructions + addRule("^\\b[A-Z0-9]+\\b", Qt::darkBlue); // Instructions addRule("-?R\\d[^,;\\s]*", Qt::darkRed); // -R0.* addRule("-?H\\d[^,;\\s]*", Qt::red); // -H1.* addRule("-?v\\[\\d\\]*[^,;\\s]*", Qt::darkCyan); // -v[xyz].* From 68c70dd1b91499ce2e70f850ba62f8cb07f6e88a Mon Sep 17 00:00:00 2001 From: Eladash Date: Thu, 24 Aug 2023 19:59:24 +0300 Subject: [PATCH 122/184] Savestates: Fix config_event_entry compatibility --- rpcs3/Emu/Cell/Modules/cellPad.cpp | 5 +++++ rpcs3/Emu/Cell/Modules/sys_io_.cpp | 18 ++++++++++++++++-- rpcs3/Emu/savestate_utils.cpp | 9 +++++++-- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellPad.cpp b/rpcs3/Emu/Cell/Modules/cellPad.cpp index 839dbb47d3..2c3f0f695b 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPad.cpp @@ -53,14 +53,19 @@ void fmt_class_string::format(std::string& out, u64 arg) }); } +extern void sys_io_serialize(utils::serial& ar); + pad_info::pad_info(utils::serial& ar) : max_connect(ar) , port_setting(ar) { + sys_io_serialize(ar); } void pad_info::save(utils::serial& ar) { + USING_SERIALIZATION_VERSION(sys_io); + ar(max_connect, port_setting); } diff --git a/rpcs3/Emu/Cell/Modules/sys_io_.cpp b/rpcs3/Emu/Cell/Modules/sys_io_.cpp index 24cccd5022..ce834549f7 100644 --- a/rpcs3/Emu/Cell/Modules/sys_io_.cpp +++ b/rpcs3/Emu/Cell/Modules/sys_io_.cpp @@ -23,16 +23,30 @@ struct libio_sys_config ~libio_sys_config() noexcept { } + + void save_or_load(utils::serial& ar) + { + ar(init_ctr, ppu_id, queue_id); + } }; +extern void sys_io_serialize(utils::serial& ar) +{ + // Do not assign a serialization tag for now, call it from cellPad serialization + g_fxo->get().save_or_load(ar); +} + extern void cellPad_NotifyStateChange(u32 index, u32 state); void config_event_entry(ppu_thread& ppu) { auto& cfg = g_fxo->get(); - // Ensure awake - ppu.check_state(); + if (!ppu.loaded_from_savestate) + { + // Ensure awake + ppu.check_state(); + } while (!sys_event_queue_receive(ppu, cfg.queue_id, vm::null, 0)) { diff --git a/rpcs3/Emu/savestate_utils.cpp b/rpcs3/Emu/savestate_utils.cpp index 9b423ed415..d2ac671cc3 100644 --- a/rpcs3/Emu/savestate_utils.cpp +++ b/rpcs3/Emu/savestate_utils.cpp @@ -18,7 +18,7 @@ struct serial_ver_t std::set compatible_versions; }; -static std::array s_serial_versions; +static std::array s_serial_versions; #define SERIALIZATION_VER(name, identifier, ...) \ \ @@ -35,7 +35,7 @@ static std::array s_serial_versions; return ::s_serial_versions[identifier].current_version;\ } -SERIALIZATION_VER(global_version, 0, 13) // For stuff not listed here +SERIALIZATION_VER(global_version, 0, 14) // For stuff not listed here SERIALIZATION_VER(ppu, 1, 1) SERIALIZATION_VER(spu, 2, 1) SERIALIZATION_VER(lv2_sync, 3, 1) @@ -73,6 +73,11 @@ SERIALIZATION_VER(cellGcm, 19, 1) SERIALIZATION_VER(sysPrxForUser, 20, 1) SERIALIZATION_VER(cellSaveData, 21, 1) SERIALIZATION_VER(cellAudioOut, 22, 1) +SERIALIZATION_VER(sys_io, 23, 1) + +// Misc versions for HLE/LLE not included so main version would not invalidated +SERIALIZATION_VER(LLE, 24, 1) +SERIALIZATION_VER(HLE, 25, 1) std::vector> get_savestate_versioning_data(const fs::file& file) { From 82c5c4d285001520caa44c960a7d24c584ce1698 Mon Sep 17 00:00:00 2001 From: Eladash Date: Thu, 24 Aug 2023 23:03:51 +0300 Subject: [PATCH 123/184] PPU Analyzer: Analyze whole segment 0 when patches are applied Improve greedy instruction search. --- rpcs3/Emu/Cell/Modules/cellPad.cpp | 2 + rpcs3/Emu/Cell/PPUAnalyser.cpp | 124 ++++++++++++++++++++++------- rpcs3/Emu/Cell/PPUAnalyser.h | 15 +++- rpcs3/Emu/Cell/PPUModule.cpp | 102 +++++++++++++++++++----- 4 files changed, 194 insertions(+), 49 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellPad.cpp b/rpcs3/Emu/Cell/Modules/cellPad.cpp index 2c3f0f695b..107be92a9d 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPad.cpp @@ -67,6 +67,8 @@ void pad_info::save(utils::serial& ar) USING_SERIALIZATION_VERSION(sys_io); ar(max_connect, port_setting); + + sys_io_serialize(ar); } extern void send_sys_io_connect_event(u32 index, u32 state); diff --git a/rpcs3/Emu/Cell/PPUAnalyser.cpp b/rpcs3/Emu/Cell/PPUAnalyser.cpp index 79ff56fb62..844cd18fb3 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.cpp +++ b/rpcs3/Emu/Cell/PPUAnalyser.cpp @@ -1390,9 +1390,9 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b block.second = _ptr.addr() - block.first; break; } - else if (type == ppu_itype::TW || type == ppu_itype::TWI || type == ppu_itype::TD || type == ppu_itype::TDI) + else if (type & ppu_itype::trap) { - if (op.opcode != ppu_instructions::TRAP()) + if (op.bo != 31) { add_block(_ptr.addr()); } @@ -1618,6 +1618,8 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b end = 0; } + u32 per_instruction_bytes = 0; + for (auto&& [_, func] : as_rvalue(fmap)) { if (func.attr & ppu_attr::no_size && entry) @@ -1636,6 +1638,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b block.attr = ppu_attr::no_size; } + per_instruction_bytes += utils::sub_saturate(lim, func.addr); continue; } @@ -1716,11 +1719,8 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b u32 exp = start; u32 lim = end; - // Start with full scan (disabled for PRX for now) - if (entry) - { - block_queue.emplace_back(exp, lim); - } + // Start with full scan + block_queue.emplace_back(exp, lim); // Add entries from patches (on per-instruction basis) for (u32 addr : applied) @@ -1754,14 +1754,17 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b { u32 i_pos = exp; + u32 block_edges[16]; + u32 edge_count = 0; + bool is_good = true; bool is_fallback = true; for (; i_pos < lim; i_pos += 4) { - const u32 opc = get_ref(i_pos); + const ppu_opcode_t op{get_ref(i_pos)}; - switch (auto type = s_ppu_itype.decode(opc)) + switch (auto type = s_ppu_itype.decode(op.opcode)) { case ppu_itype::UNK: case ppu_itype::ECIWX: @@ -1771,10 +1774,20 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b is_good = false; break; } - case ppu_itype::TD: case ppu_itype::TDI: - case ppu_itype::TW: case ppu_itype::TWI: + { + if (op.ra == 1u || op.ra == 13u || op.ra == 2u) + { + // Non-user registers, checking them against a constant value makes no sense + is_good = false; + break; + } + + [[fallthrough]]; + } + case ppu_itype::TD: + case ppu_itype::TW: case ppu_itype::B: case ppu_itype::BC: { @@ -1785,14 +1798,14 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b if (type == ppu_itype::B || type == ppu_itype::BC) { - if (entry == 0 && ppu_opcode_t{opc}.aa) + if (entry == 0 && op.aa) { // Ignore absolute branches in PIC (PRX) is_good = false; break; } - const u32 target = (opc & 2 ? 0 : i_pos) + (type == ppu_itype::B ? +ppu_opcode_t{opc}.bt24 : +ppu_opcode_t{opc}.bt14); + const u32 target = (op.aa ? 0 : i_pos) + (type == ppu_itype::B ? +op.bt24 : +op.bt14); if (target < segs[0].addr || target >= segs[0].addr + segs[0].size) { @@ -1801,9 +1814,43 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b break; } + const ppu_opcode_t test_op{get_ref(target)}; + const auto type0 = s_ppu_itype.decode(test_op.opcode); + + if (type0 == ppu_itype::UNK) + { + is_good = false; + break; + } + + // Test another instruction just in case (testing more is unlikely to improve results by much) + if (!(type0 & ppu_itype::branch)) + { + if (target + 4 >= segs[0].addr + segs[0].size) + { + is_good = false; + break; + } + + const auto type1 = s_ppu_itype.decode(get_ref(target + 4)); + + if (type1 == ppu_itype::UNK) + { + is_good = false; + break; + } + } + else if (u32 target0 = (test_op.aa ? 0 : target) + (type == ppu_itype::B ? +test_op.bt24 : +test_op.bt14); + target0 < segs[0].addr || target0 >= segs[0].addr + segs[0].size) + { + // Sanity check + is_good = false; + break; + } + if (target != i_pos && !fmap.contains(target)) { - if (block_set.count(target) == 0) + if (block_set.count(target) == 0 && std::count(block_edges, block_edges + edge_count, target) == 0) { ppu_log.trace("Block target found: 0x%x (i_pos=0x%x)", target, i_pos); block_queue.emplace_back(target, 0); @@ -1818,27 +1865,38 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b case ppu_itype::BCLR: case ppu_itype::SC: { - if (type == ppu_itype::SC && opc != ppu_instructions::SC(0)) + if (type == ppu_itype::SC && op.opcode != ppu_instructions::SC(0)) { // Strict garbage filter is_good = false; break; } - if (type == ppu_itype::BCCTR && opc & 0xe000) + if (type == ppu_itype::BCCTR && op.opcode & 0xe000) { // Garbage filter is_good = false; break; } - if (type == ppu_itype::BCLR && opc & 0xe000) + if (type == ppu_itype::BCLR && op.opcode & 0xe000) { // Garbage filter is_good = false; break; } + if ((type & ppu_itype::branch && op.lk) || type & ppu_itype::trap || type == ppu_itype::BC) + { + // if farther instructions are valid: register all blocks + // Otherwise, register none (all or nothing) + if (edge_count < std::size(block_edges)) + { + block_edges[edge_count++] = i_pos + 4; + continue; + } + } + // Good block terminator found, add single block break; } @@ -1869,17 +1927,23 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b if (is_good) { - auto& block = fmap[exp]; - - if (!block.addr) + for (u32 it = 0, prev_addr = exp; it <= edge_count; it++) { - block.addr = exp; - block.size = i_pos - exp; - ppu_log.trace("Block __0x%x added (size=0x%x)", block.addr, block.size); + const u32 block_end = it < edge_count ? block_edges[it] : i_pos; + const u32 block_begin = std::exchange(prev_addr, block_end); - if (get_limit(exp) == end) + auto& block = fmap[block_begin]; + + if (!block.addr) { - block.attr += ppu_attr::no_size; + block.addr = block_begin; + block.size = block_end - block_begin; + ppu_log.trace("Block __0x%x added (size=0x%x)", block.addr, block.size); + + if (get_limit(block_begin) == end) + { + block.attr += ppu_attr::no_size; + } } } } @@ -1902,9 +1966,8 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b // Convert map to vector (destructive) for (auto&& [_, block] : as_rvalue(std::move(fmap))) { - if (block.attr & ppu_attr::no_size && block.size > 4 && entry) + if (block.attr & ppu_attr::no_size && block.size > 4) { - // Disabled for PRX for now ppu_log.warning("Block 0x%x will be compiled on per-instruction basis (size=0x%x)", block.addr, block.size); for (u32 addr = block.addr; addr < block.addr + block.size; addr += 4) @@ -1916,12 +1979,19 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b i.attr = ppu_attr::no_size; } + per_instruction_bytes += block.size; continue; } funcs.emplace_back(std::move(block)); } + if (per_instruction_bytes) + { + const bool error = per_instruction_bytes >= 200 && per_instruction_bytes / 4 >= utils::aligned_div(funcs.size(), 128); + (error ? ppu_log.error : ppu_log.notice)("%d instructions will be compiled on per-instruction basis in total", per_instruction_bytes / 4); + } + ppu_log.notice("Block analysis: %zu blocks (%zu enqueued)", funcs.size(), block_queue.size()); return true; } diff --git a/rpcs3/Emu/Cell/PPUAnalyser.h b/rpcs3/Emu/Cell/PPUAnalyser.h index ef02faa530..ccbd743f92 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.h +++ b/rpcs3/Emu/Cell/PPUAnalyser.h @@ -263,6 +263,7 @@ struct ppu_pattern_matrix struct ppu_itype { static constexpr struct branch_tag{} branch{}; // Branch Instructions + static constexpr struct trap_tag{} trap{}; // Branch Instructions enum type { @@ -425,8 +426,6 @@ struct ppu_itype VUPKLSB, VUPKLSH, VXOR, - TDI, - TWI, MULLI, SUBFIC, CMPLI, @@ -461,7 +460,6 @@ struct ppu_itype RLDCL, RLDCR, CMP, - TW, LVSL, LVEBX, SUBFC, @@ -488,7 +486,6 @@ struct ppu_itype LWZUX, CNTLZD, ANDC, - TD, LVEWX, MULHD, MULHW, @@ -784,6 +781,11 @@ struct ppu_itype BC, BCLR, BCCTR, // branch_tag last + + TD, // trap_tag first + TW, + TDI, + TWI, // trap_tag last }; // Enable address-of operator for ppu_decoder<> @@ -796,6 +798,11 @@ struct ppu_itype { return value >= B && value <= BCCTR; } + + friend constexpr bool operator &(type value, trap_tag) + { + return value >= TD && value <= TWI; + } }; struct ppu_iname diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index 4df1749bcd..ff8f3c1c83 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -1356,12 +1356,6 @@ std::shared_ptr ppu_load_prx(const ppu_prx_object& elf, bool virtual_lo // Initialize executable code if necessary if (prog.p_flags & 0x1 && !virtual_load) { - if (ar) - { - // Disable analysis optimization for savestates (it's not compatible with savestate with patches applied) - end = std::max(end, utils::align(addr + mem_size, 0x10000)); - } - ppu_register_range(addr, mem_size); } } @@ -1651,6 +1645,36 @@ std::shared_ptr ppu_load_prx(const ppu_prx_object& elf, bool virtual_lo } } + // Disabled for PRX for now (problematic and does not seem to have any benefit) + end = 0; + + if (!applied.empty() || ar) + { + // Compare memory changes in memory after executable code sections end + if (end >= prx->segs[0].addr && end < prx->segs[0].addr + prx->segs[0].size) + { + for (const auto& prog : elf.progs) + { + // Find the first segment + if (prog.p_type == 0x1u /* LOAD */ && prog.p_memsz) + { + std::basic_string_view elf_memory{prog.bin.data(), prog.bin.size()}; + elf_memory.remove_prefix(end - prx->segs[0].addr); + + if (elf_memory != std::basic_string_view{&prx->get_ref(end), elf_memory.size()}) + { + // There are changes, disable analysis optimization + ppu_loader.notice("Disabling analysis optimization due to memory changes from original file"); + + end = 0; + } + + break; + } + } + } + } + // Embedded SPU elf patching for (const auto& seg : prx->segs) { @@ -1910,12 +1934,6 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str // Initialize executable code if necessary if (prog.p_flags & 0x1 && !virtual_load) { - if (already_loaded && ar) - { - // Disable analysis optimization for savestates (it's not compatible with savestate with patches applied) - end = std::max(end, utils::align(addr + size, 0x10000)); - } - ppu_register_range(addr, size); } } @@ -1969,6 +1987,33 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str applied += g_fxo->get().apply(Emu.GetTitleID() + '-' + hash, [&](u32 addr, u32 size) { return _main.get_ptr(addr, size); }); } + if (!applied.empty() || ar) + { + // Compare memory changes in memory after executable code sections end + if (end >= _main.segs[0].addr && end < _main.segs[0].addr + _main.segs[0].size) + { + for (const auto& prog : elf.progs) + { + // Find the first segment + if (prog.p_type == 0x1u /* LOAD */ && prog.p_memsz) + { + std::basic_string_view elf_memory{prog.bin.data(), prog.bin.size()}; + elf_memory.remove_prefix(end - _main.segs[0].addr); + + if (elf_memory != std::basic_string_view{&_main.get_ref(end), elf_memory.size()}) + { + // There are changes, disable analysis optimization + ppu_loader.notice("Disabling analysis optimization due to memory changes from original file"); + + end = 0; + } + + break; + } + } + } + } + if (applied.empty()) { ppu_loader.warning("PPU executable hash: %s", hash); @@ -2574,12 +2619,6 @@ std::pair, CellError> ppu_load_overlay(const ppu_ex // Initialize executable code if necessary if (prog.p_flags & 0x1 && !virtual_load) { - if (ar) - { - // Disable analysis optimization for savestates (it's not compatible with savestate with patches applied) - end = std::max(end, utils::align(addr + size, 0x10000)); - } - ppu_register_range(addr, size); } } @@ -2631,6 +2670,33 @@ std::pair, CellError> ppu_load_overlay(const ppu_ex applied += g_fxo->get().apply(Emu.GetTitleID() + '-' + hash, [ovlm](u32 addr, u32 size) { return ovlm->get_ptr(addr, size); }); } + if (!applied.empty() || ar) + { + // Compare memory changes in memory after executable code sections end + if (end >= ovlm->segs[0].addr && end < ovlm->segs[0].addr + ovlm->segs[0].size) + { + for (const auto& prog : elf.progs) + { + // Find the first segment + if (prog.p_type == 0x1u /* LOAD */ && prog.p_memsz) + { + std::basic_string_view elf_memory{prog.bin.data(), prog.bin.size()}; + elf_memory.remove_prefix(end - ovlm->segs[0].addr); + + if (elf_memory != std::basic_string_view{&ovlm->get_ref(end), elf_memory.size()}) + { + // There are changes, disable analysis optimization + ppu_loader.notice("Disabling analysis optimization due to memory changes from original file"); + + end = 0; + } + + break; + } + } + } + } + // Embedded SPU elf patching for (const auto& seg : ovlm->segs) { From eb61ae37ae206cc19d682fb96853fd10ec1c6727 Mon Sep 17 00:00:00 2001 From: Eladash Date: Fri, 25 Aug 2023 19:39:19 +0300 Subject: [PATCH 124/184] rsx: Optimize RET returning to following CALL --- rpcs3/Emu/RSX/RSXFIFO.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/rpcs3/Emu/RSX/RSXFIFO.cpp b/rpcs3/Emu/RSX/RSXFIFO.cpp index c4e52c6e6b..637db20913 100644 --- a/rpcs3/Emu/RSX/RSXFIFO.cpp +++ b/rpcs3/Emu/RSX/RSXFIFO.cpp @@ -701,6 +701,24 @@ namespace rsx return; } + // Optimize returning to another CALL + if ((ctrl->put & ~3) != fifo_ret_addr) + { + if (u32 addr = iomap_table.get_addr(fifo_ret_addr); addr != umax) + { + const u32 cmd0 = vm::read32(addr); + + // Check for missing step flags, in case the user is single-stepping in the debugger + if ((cmd0 & RSX_METHOD_CALL_CMD_MASK) == RSX_METHOD_CALL_CMD && cpu_flag::dbg_step - state) + { + fifo_ctrl->set_get(cmd0 & RSX_METHOD_CALL_OFFSET_MASK); + last_known_code_start = ctrl->get; + fifo_ret_addr += 4; + return; + } + } + } + fifo_ctrl->set_get(std::exchange(fifo_ret_addr, RSX_CALL_STACK_EMPTY)); last_known_code_start = ctrl->get; return; From 8bd9a52de3907522a26b199cec42abb9f1043026 Mon Sep 17 00:00:00 2001 From: Dark Date: Fri, 25 Aug 2023 11:52:06 -0400 Subject: [PATCH 125/184] RB3MidiKeyboard.cpp: fix some note off messages being processed incorrectly Some keyboards send a note on message with zero velocity instead of a note off. As the MIDI spec permits this, it needs to be handled here. --- rpcs3/Emu/Io/RB3MidiKeyboard.cpp | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/rpcs3/Emu/Io/RB3MidiKeyboard.cpp b/rpcs3/Emu/Io/RB3MidiKeyboard.cpp index 4d515cc090..1b1ee39597 100644 --- a/rpcs3/Emu/Io/RB3MidiKeyboard.cpp +++ b/rpcs3/Emu/Io/RB3MidiKeyboard.cpp @@ -199,41 +199,43 @@ void usb_device_rb3_midi_keyboard::parse_midi_message(u8* msg, usz size) // handle note on/off messages if (size == 3 && (msg[0] == 0x80 || msg[0] == 0x90)) { + bool note_on = (0x10 & msg[0]) == 0x10 && msg[2] != 0; + // handle navigation buttons switch (msg[1]) { case 44: // G#2 - button_state.cross = ((0x10 & msg[0]) == 0x10); + button_state.cross = note_on; break; case 42: // F#2 - button_state.circle = ((0x10 & msg[0]) == 0x10); + button_state.circle = note_on; break; case 39: // D#2 - button_state.square = ((0x10 & msg[0]) == 0x10); + button_state.square = note_on; break; case 37: // C#2 - button_state.triangle = ((0x10 & msg[0]) == 0x10); + button_state.triangle = note_on; break; case 46: // A#2 - button_state.start = ((0x10 & msg[0]) == 0x10); + button_state.start = note_on; break; case 36: // C2 - button_state.select = ((0x10 & msg[0]) == 0x10); + button_state.select = note_on; break; case 45: // A2 - button_state.overdrive = ((0x10 & msg[0]) == 0x10); + button_state.overdrive = note_on; break; case 41: // F2 - button_state.dpad_up = ((0x10 & msg[0]) == 0x10); + button_state.dpad_up = note_on; break; case 43: // G2 - button_state.dpad_down = ((0x10 & msg[0]) == 0x10); + button_state.dpad_down = note_on; break; case 38: // D2 - button_state.dpad_left = ((0x10 & msg[0]) == 0x10); + button_state.dpad_left = note_on; break; case 40: // E2 - button_state.dpad_right = ((0x10 & msg[0]) == 0x10); + button_state.dpad_right = note_on; break; default: break; @@ -243,7 +245,7 @@ void usb_device_rb3_midi_keyboard::parse_midi_message(u8* msg, usz size) if (msg[1] >= 48 && msg[1] <= (48 + button_state.keys.size())) { const u32 key = msg[1] - 48; - button_state.keys[key] = ((0x10 & msg[0]) == 0x10); + button_state.keys[key] = note_on; button_state.velocities[key] = msg[2]; } } From bf8621c921eba267e5f2123edc53fd3f8a4bc80a Mon Sep 17 00:00:00 2001 From: Dark Date: Fri, 25 Aug 2023 13:59:11 -0400 Subject: [PATCH 126/184] Update rpcs3/Emu/Io/RB3MidiKeyboard.cpp Co-authored-by: Megamouse --- rpcs3/Emu/Io/RB3MidiKeyboard.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/Emu/Io/RB3MidiKeyboard.cpp b/rpcs3/Emu/Io/RB3MidiKeyboard.cpp index 1b1ee39597..dd53eef0ec 100644 --- a/rpcs3/Emu/Io/RB3MidiKeyboard.cpp +++ b/rpcs3/Emu/Io/RB3MidiKeyboard.cpp @@ -199,7 +199,7 @@ void usb_device_rb3_midi_keyboard::parse_midi_message(u8* msg, usz size) // handle note on/off messages if (size == 3 && (msg[0] == 0x80 || msg[0] == 0x90)) { - bool note_on = (0x10 & msg[0]) == 0x10 && msg[2] != 0; + const bool note_on = (0x10 & msg[0]) == 0x10 && msg[2] != 0; // handle navigation buttons switch (msg[1]) From f4c2b4cc7ebf3e3a9d0562f7b55127ac2d9f10f2 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 25 Aug 2023 22:55:30 +0200 Subject: [PATCH 127/184] GHLtar: fix guitar tilt fixes #14454 :facepalm: --- rpcs3/Emu/Io/ghltar_config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/Emu/Io/ghltar_config.h b/rpcs3/Emu/Io/ghltar_config.h index 8ed23ead1d..524542fa9f 100644 --- a/rpcs3/Emu/Io/ghltar_config.h +++ b/rpcs3/Emu/Io/ghltar_config.h @@ -43,7 +43,7 @@ struct cfg_ghltar final : public emulated_pad_config cfg_pad_btn dpad_left{ this, "D-Pad Left", ghltar_btn::dpad_left, pad_button::dpad_left }; cfg_pad_btn dpad_right{ this, "D-Pad Right", ghltar_btn::dpad_right, pad_button::dpad_right }; cfg_pad_btn whammy{ this, "Whammy", ghltar_btn::whammy, pad_button::rs_y }; - cfg_pad_btn tilt{ this, "tilt", ghltar_btn::whammy, pad_button::rs_x }; + cfg_pad_btn tilt{ this, "tilt", ghltar_btn::tilt, pad_button::rs_x }; }; struct cfg_ghltars final : public emulated_pads_config From c72779588111e194476c6b9d996f576eac19c0ac Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 26 Aug 2023 09:56:50 +0200 Subject: [PATCH 128/184] input: clamp 0-1 in NormalizeDirectedInput If you called this function with a value smaller than threshold, you would get an overflow. This never happened, because we always passed values bigger than threshold. Let's better fix this anyway. --- rpcs3/Emu/Io/PadHandler.cpp | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/rpcs3/Emu/Io/PadHandler.cpp b/rpcs3/Emu/Io/PadHandler.cpp index 40eb6dc000..b04e2b6f79 100644 --- a/rpcs3/Emu/Io/PadHandler.cpp +++ b/rpcs3/Emu/Io/PadHandler.cpp @@ -55,15 +55,14 @@ u16 PadHandlerBase::NormalizeTriggerInput(u16 value, int threshold) const { return static_cast(0); } - else if (threshold <= trigger_min) + + if (threshold <= trigger_min) { return static_cast(ScaledInput(value, trigger_min, trigger_max)); } - else - { - const s32 val = static_cast(static_cast(trigger_max) * (value - threshold) / (trigger_max - threshold)); - return static_cast(ScaledInput(val, trigger_min, trigger_max)); - } + + const s32 val = static_cast(static_cast(trigger_max) * (value - threshold) / (trigger_max - threshold)); + return static_cast(ScaledInput(val, trigger_min, trigger_max)); } // normalizes a directed input, meaning it will correspond to a single "button" and not an axis with two directions @@ -81,11 +80,9 @@ u16 PadHandlerBase::NormalizeDirectedInput(s32 raw_value, s32 threshold, s32 max { return static_cast(255.0f * val); } - else - { - const f32 thresh = static_cast(threshold) / maximum; // threshold converted to [0, 1] - return static_cast(255.0f * std::min(1.0f, (val - thresh) / (1.0f - thresh))); - } + + const f32 thresh = static_cast(threshold) / maximum; // threshold converted to [0, 1] + return static_cast(255.0f * std::clamp((val - thresh) / (1.0f - thresh), 0.0f, 1.0f)); } u16 PadHandlerBase::NormalizeStickInput(u16 raw_value, int threshold, int multiplier, bool ignore_threshold) const @@ -96,10 +93,8 @@ u16 PadHandlerBase::NormalizeStickInput(u16 raw_value, int threshold, int multip { return static_cast(ScaledInput(scaled_value, 0, thumb_max)); } - else - { - return NormalizeDirectedInput(scaled_value, threshold, thumb_max); - } + + return NormalizeDirectedInput(scaled_value, threshold, thumb_max); } // This function normalizes stick deadzone based on the DS3's deadzone, which is ~13% From b2484838418a523c6f8791b316eb1d56e937d461 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 26 Aug 2023 10:07:28 +0200 Subject: [PATCH 129/184] input: implement pressure intensity deadzone --- rpcs3/Emu/Io/PadHandler.cpp | 13 ++++- rpcs3/Emu/Io/pad_config.h | 1 + rpcs3/Emu/Io/pad_types.cpp | 2 +- rpcs3/Emu/Io/pad_types.h | 2 +- rpcs3/Input/evdev_joystick_handler.cpp | 13 ++++- rpcs3/Input/keyboard_pad_handler.cpp | 23 ++++++-- rpcs3/Input/keyboard_pad_handler.h | 3 +- rpcs3/rpcs3qt/pad_settings_dialog.cpp | 8 +++ rpcs3/rpcs3qt/pad_settings_dialog.ui | 72 ++++++++++++++++---------- rpcs3/rpcs3qt/tooltips.h | 1 + 10 files changed, 98 insertions(+), 40 deletions(-) diff --git a/rpcs3/Emu/Io/PadHandler.cpp b/rpcs3/Emu/Io/PadHandler.cpp index b04e2b6f79..af344da494 100644 --- a/rpcs3/Emu/Io/PadHandler.cpp +++ b/rpcs3/Emu/Io/PadHandler.cpp @@ -578,7 +578,8 @@ void PadHandlerBase::get_mapping(const pad_ensemble& binding) // Find out if special buttons are pressed (introduced by RPCS3). // These buttons will have a delay of one cycle, but whatever. - const bool adjust_pressure = pad->get_pressure_intensity_enabled(cfg->pressure_intensity_toggle_mode.get()); + const bool adjust_pressure = pad->get_pressure_intensity_button_active(cfg->pressure_intensity_toggle_mode.get()); + const u32 pressure_intensity_deadzone = cfg->pressure_intensity_deadzone.get(); // Translate any corresponding keycodes to our normal DS3 buttons and triggers for (Button& button : pad->m_buttons) @@ -600,9 +601,17 @@ void PadHandlerBase::get_mapping(const pad_ensemble& binding) { val = pad->m_pressure_intensity; } + else if (pressure_intensity_deadzone > 0) + { + // Ignore triggers, since they have their own deadzones + if (!get_is_left_trigger(device, code) && !get_is_right_trigger(device, code)) + { + val = NormalizeDirectedInput(val, pressure_intensity_deadzone, 255); + } + } value = std::max(value, val); - pressed = true; + pressed = value > 0; } } diff --git a/rpcs3/Emu/Io/pad_config.h b/rpcs3/Emu/Io/pad_config.h index bf9bcf7e91..614b394237 100644 --- a/rpcs3/Emu/Io/pad_config.h +++ b/rpcs3/Emu/Io/pad_config.h @@ -62,6 +62,7 @@ struct cfg_pad final : cfg::node cfg::string pressure_intensity_button{ this, "Pressure Intensity Button", "" }; cfg::uint<0, 100> pressure_intensity{ this, "Pressure Intensity Percent", 50 }; cfg::_bool pressure_intensity_toggle_mode{ this, "Pressure Intensity Toggle Mode", false }; + cfg::uint<0, 255> pressure_intensity_deadzone{ this, "Pressure Intensity Deadzone", 0 }; cfg::uint<0, 200> lstickmultiplier{ this, "Left Stick Multiplier", 100 }; cfg::uint<0, 200> rstickmultiplier{ this, "Right Stick Multiplier", 100 }; diff --git a/rpcs3/Emu/Io/pad_types.cpp b/rpcs3/Emu/Io/pad_types.cpp index b62fee2f57..43ab5ffcb7 100644 --- a/rpcs3/Emu/Io/pad_types.cpp +++ b/rpcs3/Emu/Io/pad_types.cpp @@ -132,7 +132,7 @@ u32 get_axis_keycode(u32 offset, u16 value) } } -bool Pad::get_pressure_intensity_enabled(bool is_toggle_mode) +bool Pad::get_pressure_intensity_button_active(bool is_toggle_mode) { if (m_pressure_intensity_button_index < 0) { diff --git a/rpcs3/Emu/Io/pad_types.h b/rpcs3/Emu/Io/pad_types.h index 916f30a559..abc7dac077 100644 --- a/rpcs3/Emu/Io/pad_types.h +++ b/rpcs3/Emu/Io/pad_types.h @@ -430,7 +430,7 @@ struct Pad bool m_pressure_intensity_toggled{}; // Whether the sensitivity is toggled on or off. u8 m_pressure_intensity{127}; // 0-255 bool m_adjust_pressure_last{}; // only used in keyboard_pad_handler - bool get_pressure_intensity_enabled(bool is_toggle_mode); + bool get_pressure_intensity_button_active(bool is_toggle_mode); // Cable State: 0 - 1 plugged in ? u8 m_cable_state{0}; diff --git a/rpcs3/Input/evdev_joystick_handler.cpp b/rpcs3/Input/evdev_joystick_handler.cpp index 474c92ed3e..c0f616aa3b 100644 --- a/rpcs3/Input/evdev_joystick_handler.cpp +++ b/rpcs3/Input/evdev_joystick_handler.cpp @@ -1079,7 +1079,8 @@ void evdev_joystick_handler::apply_input_events(const std::shared_ptr& pad) // Find out if special buttons are pressed (introduced by RPCS3). // These buttons will have a delay of one cycle, but whatever. - const bool adjust_pressure = pad->get_pressure_intensity_enabled(cfg->pressure_intensity_toggle_mode.get()); + const bool adjust_pressure = pad->get_pressure_intensity_button_active(cfg->pressure_intensity_toggle_mode.get()); + const u32 pressure_intensity_deadzone = cfg->pressure_intensity_deadzone.get(); const auto update_values = [&](bool& pressed, u16& final_value, bool is_stick_value, u32 code, u16 val) { @@ -1095,10 +1096,18 @@ void evdev_joystick_handler::apply_input_events(const std::shared_ptr& pad) { val = pad->m_pressure_intensity; } + else if (pressure_intensity_deadzone > 0) + { + // Ignore triggers, since they have their own deadzones + if (!get_is_left_trigger(m_dev, code) && !get_is_right_trigger(m_dev, code)) + { + val = NormalizeDirectedInput(val, pressure_intensity_deadzone, 255); + } + } } - pressed = true; final_value = std::max(final_value, val); + pressed = final_value > 0; } }; diff --git a/rpcs3/Input/keyboard_pad_handler.cpp b/rpcs3/Input/keyboard_pad_handler.cpp index 713162d4fa..a341b50532 100644 --- a/rpcs3/Input/keyboard_pad_handler.cpp +++ b/rpcs3/Input/keyboard_pad_handler.cpp @@ -112,7 +112,7 @@ void keyboard_pad_handler::Key(const u32 code, bool pressed, u16 value) } } - const bool adjust_pressure = pad.get_pressure_intensity_enabled(m_pressure_intensity_toggle_mode); + const bool adjust_pressure = pad.get_pressure_intensity_button_active(m_pressure_intensity_toggle_mode); const bool adjust_pressure_changed = pad.m_adjust_pressure_last != adjust_pressure; if (adjust_pressure_changed) @@ -153,16 +153,28 @@ void keyboard_pad_handler::Key(const u32 code, bool pressed, u16 value) if (update_button) { - button.m_pressed = button.m_actual_value > 0; - - if (button.m_pressed) + if (button.m_actual_value > 0) { // Modify pressure if necessary if the button was pressed - button.m_value = adjust_pressure ? pad.m_pressure_intensity : button.m_actual_value; + if (adjust_pressure) + { + button.m_value = pad.m_pressure_intensity; + } + else if (m_pressure_intensity_deadzone > 0) + { + button.m_value = NormalizeDirectedInput(button.m_actual_value, m_pressure_intensity_deadzone, 255); + } + else + { + button.m_value = button.m_actual_value; + } + + button.m_pressed = button.m_value > 0; } else { button.m_value = 0; + button.m_pressed = false; } } } @@ -886,6 +898,7 @@ bool keyboard_pad_handler::bindPadToDevice(std::shared_ptr pad, u8 player_i m_l_stick_multiplier = cfg->lstickmultiplier; m_r_stick_multiplier = cfg->rstickmultiplier; m_pressure_intensity_toggle_mode = cfg->pressure_intensity_toggle_mode.get(); + m_pressure_intensity_deadzone = cfg->pressure_intensity_deadzone.get(); const auto find_keys = [this](const cfg::string& name) { diff --git a/rpcs3/Input/keyboard_pad_handler.h b/rpcs3/Input/keyboard_pad_handler.h index 444b772bd4..60ea9c57f6 100644 --- a/rpcs3/Input/keyboard_pad_handler.h +++ b/rpcs3/Input/keyboard_pad_handler.h @@ -116,7 +116,8 @@ private: steady_clock::time_point m_button_time; f32 m_analog_lerp_factor = 1.0f; f32 m_trigger_lerp_factor = 1.0f; - bool m_pressure_intensity_toggle_mode{}; + bool m_pressure_intensity_toggle_mode = false; + u32 m_pressure_intensity_deadzone = 0; // Stick Movements steady_clock::time_point m_stick_time; diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.cpp b/rpcs3/rpcs3qt/pad_settings_dialog.cpp index 717ca4c8b6..88545214b1 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/pad_settings_dialog.cpp @@ -1178,6 +1178,11 @@ void pad_settings_dialog::UpdateLabels(bool is_reset) // Update pressure sensitivity toggle mode ui->cb_pressure_intensity_toggle_mode->setChecked(cfg.pressure_intensity_toggle_mode.get()); + // Update pressure sensitivity deadzone + range = cfg.pressure_intensity_deadzone.to_list(); + ui->pressure_intensity_deadzone->setRange(std::stoi(range.front()), std::stoi(range.back())); + ui->pressure_intensity_deadzone->setValue(cfg.pressure_intensity_deadzone.get()); + // Apply stored/default LED settings to the device m_enable_led = m_handler->has_led(); SetPadData(0, 0); @@ -1813,6 +1818,8 @@ void pad_settings_dialog::ApplyCurrentPlayerConfig(int new_player_id) cfg.pressure_intensity_toggle_mode.set(ui->cb_pressure_intensity_toggle_mode->isChecked()); } + cfg.pressure_intensity_deadzone.set(ui->pressure_intensity_deadzone->value()); + if (m_handler->m_type == pad_handler::keyboard) { const int mouse_move_mode = ui->mouse_movement->currentData().toInt(); @@ -1961,6 +1968,7 @@ void pad_settings_dialog::SubscribeTooltips() const Tooltips tooltips; SubscribeTooltip(ui->gb_pressure_intensity, tooltips.gamepad_settings.pressure_intensity); + SubscribeTooltip(ui->gb_pressure_intensity_deadzone, tooltips.gamepad_settings.pressure_deadzone); SubscribeTooltip(ui->gb_squircle, tooltips.gamepad_settings.squircle_factor); SubscribeTooltip(ui->gb_stick_multi, tooltips.gamepad_settings.stick_multiplier); SubscribeTooltip(ui->gb_kb_stick_multi, tooltips.gamepad_settings.stick_multiplier); diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.ui b/rpcs3/rpcs3qt/pad_settings_dialog.ui index 6000014044..b7c698ced7 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.ui +++ b/rpcs3/rpcs3qt/pad_settings_dialog.ui @@ -717,6 +717,22 @@
+ + + + Pressure Sensitivity Deadzone + + + + + + Qt::Horizontal + + + + + + @@ -850,34 +866,6 @@
- - - - Stick Preview - - - - 5 - - - 5 - - - 5 - - - 5 - - - - - Show Emulated Values - - - - - - @@ -2369,6 +2357,34 @@
+ + + + Stick Preview + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Show Emulated Values + + + + + + diff --git a/rpcs3/rpcs3qt/tooltips.h b/rpcs3/rpcs3qt/tooltips.h index 7850177f9e..1982a108e0 100644 --- a/rpcs3/rpcs3qt/tooltips.h +++ b/rpcs3/rpcs3qt/tooltips.h @@ -270,6 +270,7 @@ public: const QString sdl = tr("The SDL handler supports a variety of controllers across different platforms."); const QString pressure_intensity = tr("Controls the intensity of pressure sensitive buttons while this special button is pressed.
Enable \"Toggle\" if you want to toggle the intensity on button press instead.
Use the percentage to change how hard you want to press a button."); + const QString pressure_deadzone = tr("Controls the deadzone of pressure sensitive buttons. It determines how far the button has to be pressed until it is recognized by the game. The resulting range will be projected onto the full button sensitivity range."); const QString squircle_factor = tr("The actual DualShock 3's stick range is not circular but formed like a rounded square (or squircle) which represents the maximum range of the emulated sticks. You can use the squircle values to modify the stick input if your sticks can't reach the corners of that range. A value of 0 does not apply any so called squircling. A value of 8000 is usually recommended."); const QString stick_multiplier = tr("The stick multipliers can be used to change the sensitivity of your stick movements.
The default setting is 1 and represents normal input."); const QString stick_deadzones = tr("A stick's deadzone determines how far the stick has to be moved until it is fully recognized by the game. The resulting range will be projected onto the full input range in order to give you a smooth experience. Movement inside the deadzone is actually simulated as a real DualShock 3's deadzone of ~13%, so don't worry if there is still movement shown in the emulated stick preview."); From a101f6490f9c8b810e39b8215157f4dfc90a6fac Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 26 Aug 2023 15:47:52 +0200 Subject: [PATCH 130/184] input: Use pad class instead of index in pad settings dialog While not a bug, it's not very elegant to use the index as long as there isn't any gap between the values. So let's use the class instead. --- rpcs3/Emu/Io/pad_config.h | 4 ++-- rpcs3/rpcs3qt/pad_settings_dialog.cpp | 34 +++++++++++++-------------- rpcs3/rpcs3qt/pad_settings_dialog.h | 2 +- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/rpcs3/Emu/Io/pad_config.h b/rpcs3/Emu/Io/pad_config.h index 614b394237..bdde9fb219 100644 --- a/rpcs3/Emu/Io/pad_config.h +++ b/rpcs3/Emu/Io/pad_config.h @@ -1,6 +1,6 @@ #pragma once -#include "pad_config_types.h" +#include "pad_types.h" #include "Utilities/Config.h" @@ -97,7 +97,7 @@ struct cfg_pad final : cfg::node cfg::uint<0, 100> analog_lerp_factor{ this, "Analog Button Lerp Factor", 100 }; cfg::uint<0, 100> trigger_lerp_factor{ this, "Trigger Lerp Factor", 100 }; - cfg::uint<0, 5> device_class_type{ this, "Device Class Type", 0 }; + cfg::uint device_class_type{ this, "Device Class Type", 0 }; cfg::uint<0, 65535> vendor_id{ this, "Vendor ID", 0 }; cfg::uint<0, 65535> product_id{ this, "Product ID", 0 }; }; diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.cpp b/rpcs3/rpcs3qt/pad_settings_dialog.cpp index 88545214b1..9aa5afe430 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/pad_settings_dialog.cpp @@ -174,14 +174,18 @@ pad_settings_dialog::pad_settings_dialog(std::shared_ptr gui_setti // Refresh Button connect(ui->b_refresh, &QPushButton::clicked, this, &pad_settings_dialog::RefreshHandlers); - ui->chooseClass->addItem(tr("Standard (Pad)")); // CELL_PAD_PCLASS_TYPE_STANDARD = 0x00, - ui->chooseClass->addItem(tr("Guitar")); // CELL_PAD_PCLASS_TYPE_GUITAR = 0x01, - ui->chooseClass->addItem(tr("Drum")); // CELL_PAD_PCLASS_TYPE_DRUM = 0x02, - ui->chooseClass->addItem(tr("DJ")); // CELL_PAD_PCLASS_TYPE_DJ = 0x03, - ui->chooseClass->addItem(tr("Dance Mat")); // CELL_PAD_PCLASS_TYPE_DANCEMAT = 0x04, - ui->chooseClass->addItem(tr("Navigation")); // CELL_PAD_PCLASS_TYPE_NAVIGATION = 0x05, + ui->chooseClass->addItem(tr("Standard (Pad)"), u32{CELL_PAD_PCLASS_TYPE_STANDARD}); + ui->chooseClass->addItem(tr("Guitar"), u32{CELL_PAD_PCLASS_TYPE_GUITAR}); + ui->chooseClass->addItem(tr("Drum"), u32{CELL_PAD_PCLASS_TYPE_DRUM}); + ui->chooseClass->addItem(tr("DJ"), u32{CELL_PAD_PCLASS_TYPE_DJ}); + ui->chooseClass->addItem(tr("Dance Mat"), u32{CELL_PAD_PCLASS_TYPE_DANCEMAT}); + ui->chooseClass->addItem(tr("Navigation"), u32{CELL_PAD_PCLASS_TYPE_NAVIGATION}); - connect(ui->chooseClass, QOverload::of(&QComboBox::currentIndexChanged), this, &pad_settings_dialog::HandleDeviceClassChange); + connect(ui->chooseClass, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int index) + { + if (index < 0) return; + HandleDeviceClassChange(ui->chooseClass->currentData().toUInt()); + }); ui->chb_show_emulated_values->setChecked(m_gui_settings->GetValue(gui::pads_show_emulated).toBool()); @@ -1075,10 +1079,11 @@ void pad_settings_dialog::UpdateLabels(bool is_reset) const cfg_pad& cfg = GetPlayerConfig(); // Update device class - ui->chooseClass->setCurrentIndex(cfg.device_class_type); + const int index = ui->chooseClass->findData(cfg.device_class_type.get()); + ui->chooseClass->setCurrentIndex(index); // Trigger the change manually in case that the class dropdown didn't fire an event - HandleDeviceClassChange(ui->chooseClass->currentIndex()); + HandleDeviceClassChange(cfg.device_class_type); const auto products = input::get_products_by_class(cfg.device_class_type); @@ -1608,16 +1613,11 @@ void pad_settings_dialog::ChangeDevice(int index) } } -void pad_settings_dialog::HandleDeviceClassChange(int index) const +void pad_settings_dialog::HandleDeviceClassChange(u32 class_id) const { - if (index < 0) - { - return; - } - ui->chooseProduct->clear(); - for (const input::product_info& product : input::get_products_by_class(index)) + for (const input::product_info& product : input::get_products_by_class(class_id)) { switch (product.type) { @@ -1833,7 +1833,7 @@ void pad_settings_dialog::ApplyCurrentPlayerConfig(int new_player_id) cfg.r_stick_lerp_factor.set(ui->right_stick_lerp->value() * 100); } - cfg.device_class_type.set(ui->chooseClass->currentIndex()); + cfg.device_class_type.set(ui->chooseClass->currentData().toUInt()); const auto info = input::get_product_info(static_cast(ui->chooseProduct->currentData().toInt())); diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.h b/rpcs3/rpcs3qt/pad_settings_dialog.h index 5d92b83285..0be65555a1 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.h +++ b/rpcs3/rpcs3qt/pad_settings_dialog.h @@ -99,7 +99,7 @@ private Q_SLOTS: void ChangeHandler(); void ChangeProfile(const QString& profile); void ChangeDevice(int index); - void HandleDeviceClassChange(int index) const; + void HandleDeviceClassChange(u32 class_id) const; void AddProfile(); /** Update the current player config with the GUI values. */ void ApplyCurrentPlayerConfig(int new_player_id); From 08d9cbfe48ae66dad962a388b5c4581e6f347d80 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 26 Aug 2023 18:25:56 +0200 Subject: [PATCH 131/184] cellPad: implement cellPadPeriphGetData default logic and add some comments --- rpcs3/Emu/Cell/Modules/cellPad.cpp | 164 ++++++++++++++++++++++------- 1 file changed, 128 insertions(+), 36 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellPad.cpp b/rpcs3/Emu/Cell/Modules/cellPad.cpp index 107be92a9d..249bbfaef1 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPad.cpp @@ -260,39 +260,7 @@ error_code cellPadClearBuf(u32 port_no) return CELL_OK; } -void pad_get_data(u32 port_no, CellPadData* data); - -error_code cellPadGetData(u32 port_no, vm::ptr data) -{ - sys_io.trace("cellPadGetData(port_no=%d, data=*0x%x)", port_no, data); - - std::lock_guard lock(pad::g_pad_mutex); - - auto& config = g_fxo->get(); - - if (!config.max_connect) - return CELL_PAD_ERROR_UNINITIALIZED; - - const auto handler = pad::get_current_handler(); - - if (port_no >= CELL_MAX_PADS || !data) - return CELL_PAD_ERROR_INVALID_PARAMETER; - - const auto& pads = handler->GetPads(); - - if (port_no >= config.get_max_connect()) - return CELL_PAD_ERROR_NO_DEVICE; - - const auto& pad = pads[port_no]; - - if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) - return not_an_error(CELL_PAD_ERROR_NO_DEVICE); - - pad_get_data(port_no, data.get_ptr()); - return CELL_OK; -} - -void pad_get_data(u32 port_no, CellPadData* data) +void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false) { auto& config = g_fxo->get(); const auto handler = pad::get_current_handler(); @@ -305,7 +273,7 @@ void pad_get_data(u32 port_no, CellPadData* data) return; } - const auto setting = config.port_setting[port_no]; + const u32 setting = config.port_setting[port_no]; bool btnChanged = false; if (rinfo.ignore_input || !is_input_allowed()) @@ -492,6 +460,131 @@ void pad_get_data(u32 port_no, CellPadData* data) data->button[CELL_PAD_BTN_OFFSET_SENSOR_G] = pad->m_sensor_g; } } + + if (!get_periph_data || data->len <= CELL_PAD_LEN_CHANGE_SENSOR_ON) + { + return; + } + + const auto get_pressure_value = [setting](u16 val, u16 min, u16 max) -> u16 + { + if (setting & CELL_PAD_SETTING_PRESS_ON) + { + return std::clamp(val, min, max); + } + + if (val > 0) + { + return max; + } + + return 0; + }; + + // TODO: support for 'unique' controllers, which goes in offsets 24+ in padData (CELL_PAD_PCLASS_BTN_OFFSET) + // TODO: update data->len accordingly + + switch (pad->m_class_profile) + { + default: + case CELL_PAD_PCLASS_TYPE_STANDARD: + case CELL_PAD_PCLASS_TYPE_NAVIGATION: + { + break; + } + case CELL_PAD_PCLASS_TYPE_GUITAR: + { + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_1] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_2] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_3] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_4] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_5] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_STRUM_UP] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_STRUM_DOWN] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_WHAMMYBAR] = 0x80; // 0x80 – 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_H1] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_H2] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_H3] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_H4] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_H5] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_5WAY_EFFECT] = 0x0019; // One of 5 values: 0x0019, 0x004C, 0x007F (or 0x0096), 0x00B2, 0x00E5 (or 0x00E2) + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_TILT_SENS] = get_pressure_value(0, 0x0, 0xFF); + break; + } + case CELL_PAD_PCLASS_TYPE_DRUM: + { + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DRUM_SNARE] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DRUM_TOM] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DRUM_TOM2] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DRUM_TOM_FLOOR] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DRUM_KICK] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DRUM_CYM_HiHAT] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DRUM_CYM_CRASH] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DRUM_CYM_RIDE] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DRUM_KICK2] = get_pressure_value(0, 0x0, 0xFF); + break; + } + case CELL_PAD_PCLASS_TYPE_DJ: + { + // First deck + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_MIXER_ATTACK] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_MIXER_CROSSFADER] = 0; // 0x0 - 0x3FF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_MIXER_DSP_DIAL] = 0; // 0x0 - 0x3FF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_DECK1_STREAM1] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_DECK1_STREAM2] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_DECK1_STREAM3] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_DECK1_PLATTER] = 0x80; // 0x0 - 0xFF (neutral: 0x80) + + // Second deck + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_DECK2_STREAM1] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_DECK2_STREAM2] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_DECK2_STREAM3] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_DECK2_PLATTER] = 0x80; // 0x0 - 0xFF (neutral: 0x80) + break; + } + case CELL_PAD_PCLASS_TYPE_DANCEMAT: + { + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DANCEMAT_CIRCLE] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DANCEMAT_CROSS] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DANCEMAT_TRIANGLE] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DANCEMAT_SQUARE] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DANCEMAT_RIGHT] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DANCEMAT_LEFT] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DANCEMAT_UP] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DANCEMAT_DOWN] = 0; // 0x0 or 0xFF + break; + } + } +} + +error_code cellPadGetData(u32 port_no, vm::ptr data) +{ + sys_io.trace("cellPadGetData(port_no=%d, data=*0x%x)", port_no, data); + + std::lock_guard lock(pad::g_pad_mutex); + + auto& config = g_fxo->get(); + + if (!config.max_connect) + return CELL_PAD_ERROR_UNINITIALIZED; + + const auto handler = pad::get_current_handler(); + + if (port_no >= CELL_MAX_PADS || !data) + return CELL_PAD_ERROR_INVALID_PARAMETER; + + const auto& pads = handler->GetPads(); + + if (port_no >= config.get_max_connect()) + return CELL_PAD_ERROR_NO_DEVICE; + + const auto& pad = pads[port_no]; + + if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + return not_an_error(CELL_PAD_ERROR_NO_DEVICE); + + pad_get_data(port_no, data.get_ptr()); + return CELL_OK; } error_code cellPadPeriphGetInfo(vm::ptr info) @@ -573,12 +666,11 @@ error_code cellPadPeriphGetData(u32 port_no, vm::ptr data) if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); - pad_get_data(port_no, &data->cellpad_data); + pad_get_data(port_no, &data->cellpad_data, true); data->pclass_type = pad->m_class_type; data->pclass_profile = pad->m_class_profile; - // TODO: support for 'unique' controllers, which goes in offsets 24+ in padData (CELL_PAD_PCLASS_BTN_OFFSET) return CELL_OK; } From dad07da2e97e980a8a639977af5b4387d702f958 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 25 Aug 2023 21:16:12 +0200 Subject: [PATCH 132/184] Midi: fix data type warnings --- rpcs3/Emu/Io/RB3MidiGuitar.h | 2 +- rpcs3/Emu/Io/RB3MidiKeyboard.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rpcs3/Emu/Io/RB3MidiGuitar.h b/rpcs3/Emu/Io/RB3MidiGuitar.h index 3dbc712541..3a93f95d61 100644 --- a/rpcs3/Emu/Io/RB3MidiGuitar.h +++ b/rpcs3/Emu/Io/RB3MidiGuitar.h @@ -7,7 +7,7 @@ class usb_device_rb3_midi_guitar : public usb_device_emulated { private: - u32 response_pos = 0; + usz response_pos = 0; bool buttons_enabled = false; RtMidiInPtr midi_in{}; diff --git a/rpcs3/Emu/Io/RB3MidiKeyboard.h b/rpcs3/Emu/Io/RB3MidiKeyboard.h index 13fb63f5a1..c5042af239 100644 --- a/rpcs3/Emu/Io/RB3MidiKeyboard.h +++ b/rpcs3/Emu/Io/RB3MidiKeyboard.h @@ -7,7 +7,7 @@ class usb_device_rb3_midi_keyboard : public usb_device_emulated { private: - u32 response_pos = 0; + usz response_pos = 0; bool buttons_enabled = false; RtMidiInPtr midi_in{}; From 656f971823afb3e1a8016c91929fb40f1926f0ad Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 25 Aug 2023 21:34:53 +0200 Subject: [PATCH 133/184] cellPad: some readability updates --- rpcs3/Emu/Cell/Modules/cellPad.cpp | 38 ++++++++++++++++++------------ 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellPad.cpp b/rpcs3/Emu/Cell/Modules/cellPad.cpp index 249bbfaef1..8f5cc04913 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPad.cpp @@ -617,19 +617,22 @@ error_code cellPadPeriphGetInfo(vm::ptr info) if (i >= config.get_max_connect()) break; - info->port_status[i] = config.reported_info[i].port_status; - config.reported_info[i].port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; + pad_data_internal& reported_info = config.reported_info[i]; + + info->port_status[i] = reported_info.port_status; info->port_setting[i] = config.port_setting[i]; - if (~config.reported_info[i].port_status & CELL_PAD_STATUS_CONNECTED) + reported_info.port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; + + if (~reported_info.port_status & CELL_PAD_STATUS_CONNECTED) { continue; } - info->device_capability[i] = config.reported_info[i].device_capability; - info->device_type[i] = config.reported_info[i].device_type; - info->pclass_type[i] = config.reported_info[i].pclass_type; - info->pclass_profile[i] = config.reported_info[i].pclass_profile; + info->device_capability[i] = reported_info.device_capability; + info->device_type[i] = reported_info.device_type; + info->pclass_type[i] = reported_info.pclass_type; + info->pclass_profile[i] = reported_info.pclass_profile; now_connect++; } @@ -804,16 +807,18 @@ error_code cellPadGetInfo(vm::ptr info) if (i >= config.get_max_connect()) break; - config.reported_info[i].port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; // TODO: should ASSIGN flags be cleared here? - info->status[i] = config.reported_info[i].port_status; + pad_data_internal& reported_info = config.reported_info[i]; + reported_info.port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; // TODO: should ASSIGN flags be cleared here? - if (~config.reported_info[i].port_status & CELL_PAD_STATUS_CONNECTED) + info->status[i] = reported_info.port_status; + + if (~reported_info.port_status & CELL_PAD_STATUS_CONNECTED) { continue; } - info->vendor_id[i] = config.reported_info[i].vendor_id; - info->product_id[i] = config.reported_info[i].product_id; + info->vendor_id[i] = reported_info.vendor_id; + info->product_id[i] = reported_info.product_id; now_connect++; } @@ -853,11 +858,14 @@ error_code cellPadGetInfo2(vm::ptr info) if (i >= config.get_max_connect()) break; - info->port_status[i] = config.reported_info[i].port_status; - config.reported_info[i].port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; + pad_data_internal& reported_info = config.reported_info[i]; + + info->port_status[i] = reported_info.port_status; info->port_setting[i] = config.port_setting[i]; - if (~config.reported_info[i].port_status & CELL_PAD_STATUS_CONNECTED) + reported_info.port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; + + if (~reported_info.port_status & CELL_PAD_STATUS_CONNECTED) { continue; } From 9d881025206d47176795676dcc9b10936b1beff9 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 25 Aug 2023 21:42:29 +0200 Subject: [PATCH 134/184] cellPad: fix potential out of bounds read and u32 warnings --- rpcs3/Emu/Cell/Modules/cellPad.cpp | 10 +++++----- rpcs3/Emu/Cell/Modules/sys_io_.cpp | 4 ++-- rpcs3/Emu/Io/PadHandler.cpp | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellPad.cpp b/rpcs3/Emu/Cell/Modules/cellPad.cpp index 8f5cc04913..13db93e679 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPad.cpp @@ -71,9 +71,9 @@ void pad_info::save(utils::serial& ar) sys_io_serialize(ar); } -extern void send_sys_io_connect_event(u32 index, u32 state); +extern void send_sys_io_connect_event(usz index, u32 state); -void cellPad_NotifyStateChange(u32 index, u32 state) +void cellPad_NotifyStateChange(usz index, u32 state) { auto info = g_fxo->try_get(); @@ -84,7 +84,7 @@ void cellPad_NotifyStateChange(u32 index, u32 state) std::lock_guard lock(pad::g_pad_mutex); - if (!info->max_connect) + if (index >= info->get_max_connect()) { return; } @@ -148,7 +148,7 @@ void cellPad_NotifyStateChange(u32 index, u32 state) } } -extern void pad_state_notify_state_change(u32 index, u32 state) +extern void pad_state_notify_state_change(usz index, u32 state) { cellPad_NotifyStateChange(index, state); } @@ -179,7 +179,7 @@ error_code cellPadInit(ppu_thread& ppu, u32 max_connect) const auto& pads = handler->GetPads(); - for (u32 i = 0; i < statuses.size(); ++i) + for (usz i = 0; i < statuses.size(); ++i) { if (i >= config.get_max_connect()) break; diff --git a/rpcs3/Emu/Cell/Modules/sys_io_.cpp b/rpcs3/Emu/Cell/Modules/sys_io_.cpp index ce834549f7..fd69113e43 100644 --- a/rpcs3/Emu/Cell/Modules/sys_io_.cpp +++ b/rpcs3/Emu/Cell/Modules/sys_io_.cpp @@ -36,7 +36,7 @@ extern void sys_io_serialize(utils::serial& ar) g_fxo->get().save_or_load(ar); } -extern void cellPad_NotifyStateChange(u32 index, u32 state); +extern void cellPad_NotifyStateChange(usz index, u32 state); void config_event_entry(ppu_thread& ppu) { @@ -101,7 +101,7 @@ std::unique_lock lock_lv2_mutex_alike(shared_mutex& mtx, ppu_threa return lock; } -extern void send_sys_io_connect_event(u32 index, u32 state) +extern void send_sys_io_connect_event(usz index, u32 state) { auto& cfg = g_fxo->get(); diff --git a/rpcs3/Emu/Io/PadHandler.cpp b/rpcs3/Emu/Io/PadHandler.cpp index af344da494..5cf84db6f9 100644 --- a/rpcs3/Emu/Io/PadHandler.cpp +++ b/rpcs3/Emu/Io/PadHandler.cpp @@ -7,7 +7,7 @@ cfg_input g_cfg_input; -extern void pad_state_notify_state_change(u32 index, u32 state); +extern void pad_state_notify_state_change(usz index, u32 state); PadHandlerBase::PadHandlerBase(pad_handler type) : m_type(type) { From 75af7dc269f63d26ec98855182d27f8f02618868 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 25 Aug 2023 22:12:30 +0200 Subject: [PATCH 135/184] cellPad: fix lost status during cellPad_NotifyStateChange CELL_PAD_STATUS_CUSTOM_CONTROLLER was lost when setting reported_status. --- rpcs3/Emu/Cell/Modules/cellPad.cpp | 37 +++++++++++++++++------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellPad.cpp b/rpcs3/Emu/Cell/Modules/cellPad.cpp index 13db93e679..b960db944d 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPad.cpp @@ -73,7 +73,7 @@ void pad_info::save(utils::serial& ar) extern void send_sys_io_connect_event(usz index, u32 state); -void cellPad_NotifyStateChange(usz index, u32 state) +void cellPad_NotifyStateChange(usz index, u32 /*state*/) { auto info = g_fxo->try_get(); @@ -90,32 +90,37 @@ void cellPad_NotifyStateChange(usz index, u32 state) } const auto handler = pad::get_current_handler(); - const auto& pads = handler->GetPads(); + const auto& pad = pads[index]; - const u32 old = info->reported_info[index].port_status; + pad_data_internal& reported_info = info->reported_info[index]; + const u32 old_status = reported_info.port_status; // Ignore sent status for now, use the latest instead - state = (pads[index]->m_port_status & CELL_PAD_STATUS_CONNECTED); + // NOTE 1: The state's CONNECTED bit should currently be identical to the current + // m_port_status CONNECTED bit when called from our pad handlers. + // NOTE 2: Make sure to propagate all other status bits to the reported status. + const u32 new_status = pads[index]->m_port_status; - if (~(old ^ state) & CELL_PAD_STATUS_CONNECTED) + if (~(old_status ^ new_status) & CELL_PAD_STATUS_CONNECTED) { + // old and new have the same connection status return; } - info->reported_info[index].port_status = (state & CELL_PAD_STATUS_CONNECTED) | CELL_PAD_STATUS_ASSIGN_CHANGES; - info->reported_info[index].device_capability = pads[index]->m_device_capability; - info->reported_info[index].device_type = pads[index]->m_device_type; - info->reported_info[index].pclass_type = pads[index]->m_class_type; - info->reported_info[index].pclass_profile = pads[index]->m_class_profile; + reported_info.port_status = new_status | CELL_PAD_STATUS_ASSIGN_CHANGES; + reported_info.device_capability = pad->m_device_capability; + reported_info.device_type = pad->m_device_type; + reported_info.pclass_type = pad->m_class_type; + reported_info.pclass_profile = pad->m_class_profile; - if (pads[index]->m_vendor_id == 0 || pads[index]->m_product_id == 0) + if (pad->m_vendor_id == 0 || pad->m_product_id == 0) { // Fallback to defaults input::product_info product; - switch (pads[index]->m_class_type) + switch (pad->m_class_type) { case CELL_PAD_PCLASS_TYPE_GUITAR: product = input::get_product_info(input::product_type::red_octane_gh_guitar); @@ -138,13 +143,13 @@ void cellPad_NotifyStateChange(usz index, u32 state) break; } - info->reported_info[index].vendor_id = product.vendor_id; - info->reported_info[index].product_id = product.product_id; + reported_info.vendor_id = product.vendor_id; + reported_info.product_id = product.product_id; } else { - info->reported_info[index].vendor_id = pads[index]->m_vendor_id; - info->reported_info[index].product_id = pads[index]->m_product_id; + reported_info.vendor_id = pad->m_vendor_id; + reported_info.product_id = pad->m_product_id; } } From be0a789e7d9eba8fabcf34726cc78fc50f68e13d Mon Sep 17 00:00:00 2001 From: Eladash Date: Sat, 26 Aug 2023 12:10:25 +0300 Subject: [PATCH 136/184] PPU Analyzer: Fix OPD section validation --- rpcs3/Emu/Cell/PPUAnalyser.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUAnalyser.cpp b/rpcs3/Emu/Cell/PPUAnalyser.cpp index 844cd18fb3..2968190f41 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.cpp +++ b/rpcs3/Emu/Cell/PPUAnalyser.cpp @@ -554,7 +554,12 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b std::vector> func_queue; // Known references (within segs, addr and value alignment = 4) - std::set addr_heap{entry}; + std::set addr_heap; + + if (entry) + { + addr_heap.emplace(entry); + } auto verify_func = [&](u32 addr) { @@ -727,17 +732,17 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b const u32 _toc = ptr[1]; // Rough Table of Contents borders - //const u32 _toc_begin = _toc - 0x8000; - //const u32 _toc_end = _toc + 0x8000; + const u32 toc_begin = _toc - 0x8000; + //const u32 toc_end = _toc + 0x7ffc; // TODO: improve TOC constraints - if (_toc % 4 || !get_ptr(_toc) || _toc >= 0x40000000 || (_toc >= start && _toc < end)) + if (toc_begin % 4 || !get_ptr(toc_begin) || toc_begin >= 0x40000000 || (toc_begin >= start && toc_begin < end)) { sec_end.set(0); break; } - if (addr % 4 || addr < start || addr >= end || addr == _toc || !verify_func(_ptr.addr())) + if (addr % 4 || addr < start || addr >= end || !verify_func(_ptr.addr())) { sec_end.set(0); break; From 290ff5b8396948a711583fb6ba994706a01618cd Mon Sep 17 00:00:00 2001 From: Malcolm Jestadt Date: Fri, 21 Jul 2023 13:58:54 -0400 Subject: [PATCH 137/184] Zero register optimization for AVX-512-VBMI - Take advantage of the fact that AVX instructions zero the upper 128 bits for a nice optimization when one input vector is zeroed --- rpcs3/Emu/Cell/SPURecompiler.cpp | 50 ++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index 952fee9857..c23cec9a95 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -8674,6 +8674,20 @@ public: { if (data == v128::from8p(data._u8[0])) { + if (m_use_avx512_icl) + { + if (perm_only) + { + set_vr(op.rt4, vperm2b256to128(as, b, c)); + return; + } + + const auto m = gf2p8affineqb(c, build(0x40, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x40, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20), 0x7f); + const auto mm = select(noncast(m) >= 0, splat(0), m); + const auto ab = vperm2b256to128(as, b, c); + set_vr(op.rt4, select(noncast(c) >= 0, ab, mm)); + return; + } // See above const auto x = pshufb(build(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x80, 0x80), (c >> 4)); const auto ax = pshufb(as, c); @@ -8708,6 +8722,42 @@ public: if (m_use_avx512_icl && (op.ra != op.rb || m_interp_magn)) { + if (auto [ok, data] = get_const_vector(b.value, m_pos); ok) + { + if (data == v128::from8p(data._u8[0])) + { + if (perm_only) + { + set_vr(op.rt4, vperm2b256to128(a, b, eval(c ^ 0xf))); + return; + } + + const auto m = gf2p8affineqb(c, build(0x40, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x40, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20), 0x7f); + const auto mm = select(noncast(m) >= 0, splat(0), m); + const auto ab = vperm2b256to128(a, b, eval(c ^ 0xf)); + set_vr(op.rt4, select(noncast(c) >= 0, ab, mm)); + return; + } + } + + if (auto [ok, data] = get_const_vector(a.value, m_pos); ok) + { + if (data == v128::from8p(data._u8[0])) + { + if (perm_only) + { + set_vr(op.rt4, vperm2b256to128(b, a, eval(c ^ 0x1f))); + return; + } + + const auto m = gf2p8affineqb(c, build(0x40, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x40, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20), 0x7f); + const auto mm = select(noncast(m) >= 0, splat(0), m); + const auto ab = vperm2b256to128(b, a, eval(c ^ 0x1f)); + set_vr(op.rt4, select(noncast(c) >= 0, ab, mm)); + return; + } + } + if (perm_only) { set_vr(op.rt4, vperm2b(a, b, eval(c ^ 0xf))); From b5faf5800b0659a201fe6d3a274f7f1afadb075c Mon Sep 17 00:00:00 2001 From: Eladash Date: Sat, 26 Aug 2023 11:23:42 +0300 Subject: [PATCH 138/184] SPU LLVM Precompilation Implement function SPU function discovery in images or random SPU code --- rpcs3/Emu/Cell/PPUModule.cpp | 9 +- rpcs3/Emu/Cell/PPUThread.cpp | 4 +- rpcs3/Emu/Cell/RawSPUThread.cpp | 12 ++ rpcs3/Emu/Cell/SPUOpcodes.h | 6 +- rpcs3/Emu/Cell/SPURecompiler.cpp | 265 +++++++++++++++++++++++++++++- rpcs3/Emu/Cell/SPUThread.cpp | 59 ++++++- rpcs3/Emu/Cell/SPUThread.h | 3 +- rpcs3/Emu/system_config.h | 2 +- rpcs3/rpcs3qt/emu_settings_type.h | 4 +- rpcs3/rpcs3qt/settings_dialog.cpp | 4 +- rpcs3/rpcs3qt/settings_dialog.ui | 4 +- rpcs3/rpcs3qt/tooltips.h | 2 +- 12 files changed, 352 insertions(+), 22 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index ff8f3c1c83..62f80e42b1 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -1107,6 +1107,13 @@ static void ppu_check_patch_spu_images(const ppu_module& mod, const ppu_segment& if (prog.p_type == 0x1u /* LOAD */ && prog.p_filesz > 0u) { + if (prog.p_vaddr) + { + extern void utilize_spu_data_segment(u32 vaddr, const void* ls_data_vaddr, u32 size); + + utilize_spu_data_segment(prog.p_vaddr, (elf_header + prog.p_offset), prog.p_filesz); + } + sha1_update(&sha2, (elf_header + prog.p_offset), prog.p_filesz); } @@ -1119,7 +1126,7 @@ static void ppu_check_patch_spu_images(const ppu_module& mod, const ppu_segment& if (!name.empty()) { - fmt::append(dump, "\n\tSPUNAME: '%s'", name); + fmt::append(dump, "\n\tSPUNAME: '%s' (image addr: 0x%x)", name, seg.addr + i); } } } diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 775695f0a2..a64f59ca88 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -4030,7 +4030,7 @@ extern void ppu_initialize() const std::string mount_point = vfs::get("/dev_flash/"); - bool dev_flash_located = !Emu.GetCat().ends_with('P') && Emu.IsPathInsideDir(Emu.GetBoot(), mount_point) && g_cfg.core.ppu_llvm_precompilation; + bool dev_flash_located = !Emu.GetCat().ends_with('P') && Emu.IsPathInsideDir(Emu.GetBoot(), mount_point) && g_cfg.core.llvm_precompilation; if (compile_fw || dev_flash_located) { @@ -4050,7 +4050,7 @@ extern void ppu_initialize() } // Avoid compilation if main's cache exists or it is a standalone SELF with no PARAM.SFO - if (compile_main && g_cfg.core.ppu_llvm_precompilation && !Emu.GetTitleID().empty() && !Emu.IsChildProcess()) + if (compile_main && g_cfg.core.llvm_precompilation && !Emu.GetTitleID().empty() && !Emu.IsChildProcess()) { // Try to add all related directories const std::set dirs = Emu.GetGameDirs(); diff --git a/rpcs3/Emu/Cell/RawSPUThread.cpp b/rpcs3/Emu/Cell/RawSPUThread.cpp index f442e73f89..fa27229ca9 100644 --- a/rpcs3/Emu/Cell/RawSPUThread.cpp +++ b/rpcs3/Emu/Cell/RawSPUThread.cpp @@ -382,6 +382,18 @@ void spu_load_exec(const spu_exec_object& elf) spu->status_npc = {SPU_STATUS_RUNNING, elf.header.e_entry}; atomic_storage::release(spu->pc, elf.header.e_entry); + + const auto funcs = spu->discover_functions(spu->ls, umax); + + for (u32 addr : funcs) + { + spu_log.success("Found SPU function at: 0x%08x", addr); + } + + if (!funcs.empty()) + { + spu_log.success("Found %u SPU functions", funcs.size()); + } } void spu_load_rel_exec(const spu_rel_object& elf) diff --git a/rpcs3/Emu/Cell/SPUOpcodes.h b/rpcs3/Emu/Cell/SPUOpcodes.h index 60e3d0d1b5..cea4513e3f 100644 --- a/rpcs3/Emu/Cell/SPUOpcodes.h +++ b/rpcs3/Emu/Cell/SPUOpcodes.h @@ -26,17 +26,17 @@ union spu_opcode_t bf_t i18; // 7..24 }; -inline u32 spu_branch_target(u32 pc, u32 imm = 0) +constexpr u32 spu_branch_target(u32 pc, u32 imm = 0) { return (pc + (imm << 2)) & 0x3fffc; } -inline u32 spu_ls_target(u32 pc, u32 imm = 0) +constexpr u32 spu_ls_target(u32 pc, u32 imm = 0) { return (pc + (imm << 2)) & 0x3fff0; } -inline u32 spu_decode(u32 inst) +constexpr u32 spu_decode(u32 inst) { return inst >> 21; } diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index c23cec9a95..acef8a2747 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -516,6 +516,84 @@ spu_cache::~spu_cache() { } +struct spu_section_data +{ + struct data_t + { + u32 vaddr; + std::basic_string inst_data; + std::vector funcs; + }; + + shared_mutex mtx; + atomic_t had_been_used = false; + std::vector data; +}; + +extern void utilize_spu_data_segment(u32 vaddr, const void* ls_data_vaddr, u32 size) +{ + if (vaddr % 4) + { + return; + } + + size &= -4; + + if (!size || vaddr + size > SPU_LS_SIZE) + { + return; + } + + if (!g_cfg.core.llvm_precompilation) + { + return; + } + + g_fxo->need(); + + if (g_fxo->get().had_been_used) + { + return; + } + + std::basic_string data(size / 4, 0); + std::memcpy(data.data(), ls_data_vaddr, size); + + spu_section_data::data_t obj{vaddr, std::move(data)}; + + std::vector ls_data(SPU_LS_SIZE); + std::memcpy(ls_data.data() + vaddr, ls_data_vaddr, size); + + obj.funcs = spu_thread::discover_functions(ls_data.data(), umax); + + if (obj.funcs.empty()) + { + // Nothing to add + return; + } + + for (u32 addr : obj.funcs) + { + spu_log.notice("Found SPU function at: 0x%05x", addr); + } + + spu_log.notice("Found %u SPU functions", obj.funcs.size()); + + std::lock_guard lock(g_fxo->get().mtx); + + for (const auto& data : g_fxo->get().data) + { + // TODO: More robust duplicates filtering + if (data.vaddr == vaddr && data.inst_data.starts_with(obj.inst_data)) + { + spu_log.notice("Avoided duplicate SPU segment"); + return; + } + } + + g_fxo->get().data.emplace_back(std::move(obj)); +} + std::deque spu_cache::get() { std::deque result; @@ -618,6 +696,11 @@ void spu_cache::initialize() atomic_t fnext{}; atomic_t fail_flag{0}; + auto data_list = std::move(g_fxo->get().data); + g_fxo->get().had_been_used = true; + + atomic_t data_indexer{}; + if (g_cfg.core.spu_decoder == spu_decoder_type::dynamic || g_cfg.core.spu_decoder == spu_decoder_type::llvm) { if (auto compiler = spu_recompiler_base::make_llvm_recompiler(11)) @@ -657,7 +740,18 @@ void spu_cache::initialize() thread_ctrl::wait_on(g_progr_ptotal, v); } - g_progr_ptotal += ::size32(func_list); + u32 add_count = ::size32(func_list); + + if (func_list.empty()) + { + for (auto& sec : data_list) + { + add_count += sec.funcs.size(); + } + } + + g_progr_ptotal += add_count; + progr.emplace("Building SPU cache..."); worker_count = rpcs3::utils::get_max_threads(); @@ -744,6 +838,7 @@ void spu_cache::initialize() { // Likely, out of JIT memory. Signal to prevent further building. fail_flag |= 1; + continue; } // Clear fake LS @@ -752,6 +847,107 @@ void spu_cache::initialize() result++; } + if (!func_list.empty() || !g_cfg.core.llvm_precompilation) + { + // Cache has already been initiated or the user does not want to precompile SPU programs + return result; + } + + u32 last_sec_idx = umax; + + for (usz func_i = data_indexer++;; func_i = data_indexer++, g_progr_pdone++) + { + u32 passed_count = 0; + u32 func_addr = 0; + u32 sec_addr = umax; + u32 sec_idx = 0; + std::basic_string_view inst_data; + + // Try to get the data this index points to + for (auto& sec : data_list) + { + if (func_i < passed_count + sec.funcs.size()) + { + sec_addr = sec.vaddr; + func_addr = ::at32(sec.funcs, func_i - passed_count); + inst_data = sec.inst_data; + break; + } + + passed_count += sec.funcs.size(); + sec_idx++; + } + + if (sec_addr == umax) + { + // End of compilation for thread + break; + } + + if (Emu.IsStopped() || fail_flag) + { + continue; + } + + if (last_sec_idx != sec_idx) + { + if (last_sec_idx != umax) + { + // Clear fake LS of previous section + auto& sec = data_list[last_sec_idx]; + std::memset(ls.data() + sec.vaddr / 4, 0, sec.inst_data.size() * 4); + } + + // Initialize LS with the entire section data + for (u32 i = 0, pos = sec_addr; i < inst_data.size(); i++, pos += 4) + { + ls[pos / 4] = std::bit_cast>(inst_data[i]); + } + + last_sec_idx = sec_idx; + } + + // Call analyser + spu_program func2 = compiler->analyse(ls.data(), func_addr); + + while (!func2.data.empty()) + { + const u32 last_inst = std::bit_cast>(func2.data.back()); + const u32 prog_size = func2.data.size(); + + if (!compiler->compile(std::move(func2))) + { + // Likely, out of JIT memory. Signal to prevent further building. + fail_flag |= 1; + break; + } + + result++; + + if (g_cfg.core.spu_block_size >= spu_block_size_type::mega) + { + // Should already take care of the entire function + break; + } + + if (auto type = g_spu_itype.decode(last_inst); + type == spu_itype::BRSL || type == spu_itype::BRASL || type == spu_itype::BISL) + { + const u32 start_new = func_addr + prog_size * 4; + + if (start_new < SPU_LS_SIZE && ls[start_new / 4] && g_spu_itype.decode(ls[start_new / 4]) != spu_itype::UNK) + { + spu_log.notice("Precompiling fallthrough to 0x%05x", start_new); + func2 = compiler->analyse(ls.data(), start_new); + func_addr = start_new; + continue; + } + } + + break; + } + } + return result; }); @@ -1904,6 +2100,63 @@ void spu_recompiler_base::old_interpreter(spu_thread& spu, void* ls, u8* /*rip*/ } } +std::vector spu_thread::discover_functions(const void* ls_start, u32 /*entry*/) +{ + std::vector calls; + calls.reserve(100); + + // Discover functions + // Use the most simple method: search for instructions that calls them + // And then filter invalid cases (does not detect tail calls) + for (u32 i = 0x10; i < SPU_LS_SIZE; i += 0x10) + { + // Search for BRSL and BRASL + // TODO: BISL + const v128 inst = read_from_ptr>(static_cast(ls_start), i); + const v128 shifted = gv_shr32(inst, 23); + const v128 eq_brsl = gv_eq32(shifted, v128::from32p(0x66)); + const v128 eq_brasl = gv_eq32(shifted, v128::from32p(0x62)); + const v128 result = eq_brsl | eq_brasl; + + if (!gv_testz(result)) + { + for (u32 j = 0; j < 4; j++) + { + if (result.u32r[j]) + { + calls.push_back(i + j * 4); + } + } + } + } + + calls.erase(std::remove_if(calls.begin(), calls.end(), [&](u32 caller) + { + // Check the validity of both the callee code and the following caller code + return !is_exec_code(caller, ls_start) || !is_exec_code(caller + 4, ls_start); + }), calls.end()); + + std::vector addrs; + + for (u32 addr : calls) + { + const spu_opcode_t op{read_from_ptr>(static_cast(ls_start), addr)}; + + const u32 func = op_branch_targets(addr, op)[0]; + + if (func == umax || std::count(addrs.begin(), addrs.end(), func)) + { + continue; + } + + addrs.push_back(func); + } + + std::sort(addrs.begin(), addrs.end()); + + return addrs; +} + spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point) { // Result: addr + raw instruction data @@ -2647,6 +2900,8 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point) } } + spu_program result2 = result; + while (lsa > 0 || limit < 0x40000) { const u32 initial_size = ::size32(result.data); @@ -3093,7 +3348,13 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point) { workload.clear(); workload.push_back(entry_point); - ensure(m_bbs.count(entry_point)); + if (!m_bbs.count(entry_point)) + { + std::string func_bad; + dump(result2, func_bad); + spu_log.error("%s", func_bad); + return {}; + } std::basic_string new_entries; diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 8ab60cec6c..9376663893 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -487,7 +487,27 @@ std::array op_branch_targets(u32 pc, spu_opcode_t op) case spu_itype::BRASL: { const int index = (type == spu_itype::BR || type == spu_itype::BRA || type == spu_itype::BRSL || type == spu_itype::BRASL ? 0 : 1); + + // if (type == spu_itype::BRASL || type == spu_itype::BRA) + // { + // res[index] = spu_branch_target(0, op.i16); + // } + // else + // { + // // Treat i16 as signed, this allows the caller to detect "overflows" and "underflows" in address in order to detect invalid branches + // // Example: + // // [0x3fffc] BR +4 -> BR 0 -> invalid + // // [0x3fffc] BR 0x3fff4 -> BR 0 -> invalid + // const u32 add = static_cast(op.si16); + // } + res[index] = (spu_branch_target(type == spu_itype::BRASL || type == spu_itype::BRA ? 0 : pc, op.i16)); + + if (res[0] == res[1]) + { + res[1] = umax; + } + break; } case spu_itype::IRET: @@ -4013,7 +4033,7 @@ bool spu_thread::check_mfc_interrupts(u32 next_pc) return false; } -bool spu_thread::is_exec_code(u32 addr, const u8* ls_ptr) +bool spu_thread::is_exec_code(u32 addr, const void* ls_ptr) { if (addr & ~0x3FFFC) { @@ -4022,8 +4042,8 @@ bool spu_thread::is_exec_code(u32 addr, const u8* ls_ptr) for (u32 i = 0; i < 30; i++) { - const u32 addr0 = addr + (i * 4); - const u32 op = read_from_ptr>(ls_ptr + addr0); + const u32 addr0 = spu_branch_target(addr); + const u32 op = read_from_ptr>(static_cast(ls_ptr) + addr0); const auto type = s_spu_itype.decode(op); if (type == spu_itype::UNK || !op) @@ -4033,9 +4053,38 @@ bool spu_thread::is_exec_code(u32 addr, const u8* ls_ptr) if (type & spu_itype::branch) { - // TODO - break; + const auto results = op_branch_targets(addr, spu_opcode_t{op}); + + if (results[0] == umax) + { + break; + } + + for (usz res_i = 1; res_i < results.size(); res_i++) + { + const u32 route_pc = results[res_i]; + + if (route_pc >= SPU_LS_SIZE) + { + continue; + } + + // Test the validity of a single instruction of the optional target + // This function can't be too slow and is unlikely to improve results by a great deal + const u32 op0 = read_from_ptr>(static_cast(ls_ptr) + route_pc); + const auto type0 = s_spu_itype.decode(op); + + if (type == spu_itype::UNK || !op) + { + return false; + } + } + + addr = spu_branch_target(results[0]); + continue; } + + addr += 4; } return true; diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 92ed38c66e..635e5655c2 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -825,7 +825,8 @@ public: void set_events(u32 bits); void set_interrupt_status(bool enable); bool check_mfc_interrupts(u32 next_pc); - static bool is_exec_code(u32 addr, const u8* ls_ptr); // Only a hint, do not rely on it other than debugging purposes + static bool is_exec_code(u32 addr, const void* ls_ptr); // Only a hint, do not rely on it other than debugging purposes + static std::vector discover_functions(const void* ls_start, u32 /*entry*/); u32 get_ch_count(u32 ch); s64 get_ch_value(u32 ch); bool set_ch_value(u32 ch, u32 value); diff --git a/rpcs3/Emu/system_config.h b/rpcs3/Emu/system_config.h index 3355c4d452..6d6ef869e7 100644 --- a/rpcs3/Emu/system_config.h +++ b/rpcs3/Emu/system_config.h @@ -28,7 +28,7 @@ struct cfg_root : cfg::node cfg::string llvm_cpu{ this, "Use LLVM CPU" }; cfg::_int<0, 1024> llvm_threads{ this, "Max LLVM Compile Threads", 0 }; cfg::_bool ppu_llvm_greedy_mode{ this, "PPU LLVM Greedy Mode", false, false }; - cfg::_bool ppu_llvm_precompilation{ this, "PPU LLVM Precompilation", true }; + cfg::_bool llvm_precompilation{ this, "LLVM Precompilation", true }; cfg::_enum thread_scheduler{this, "Thread Scheduler Mode", thread_scheduler_mode::os}; cfg::_bool set_daz_and_ftz{ this, "Set DAZ and FTZ", false }; cfg::_enum spu_decoder{ this, "SPU Decoder", spu_decoder_type::llvm }; diff --git a/rpcs3/rpcs3qt/emu_settings_type.h b/rpcs3/rpcs3qt/emu_settings_type.h index 62d67e43fd..a3371d57d9 100644 --- a/rpcs3/rpcs3qt/emu_settings_type.h +++ b/rpcs3/rpcs3qt/emu_settings_type.h @@ -19,7 +19,7 @@ enum class emu_settings_type SPUDebug, MFCDebug, MaxLLVMThreads, - PPULLVMPrecompilation, + LLVMPrecompilation, EnableTSX, AccurateGETLLAR, AccurateSpuDMA, @@ -204,7 +204,7 @@ inline static const QMap settings_location = { emu_settings_type::SPUDebug, { "Core", "SPU Debug"}}, { emu_settings_type::MFCDebug, { "Core", "MFC Debug"}}, { emu_settings_type::MaxLLVMThreads, { "Core", "Max LLVM Compile Threads"}}, - { emu_settings_type::PPULLVMPrecompilation, { "Core", "PPU LLVM Precompilation"}}, + { emu_settings_type::LLVMPrecompilation, { "Core", "LLVM Precompilation"}}, { emu_settings_type::EnableTSX, { "Core", "Enable TSX"}}, { emu_settings_type::AccurateGETLLAR, { "Core", "Accurate GETLLAR"}}, { emu_settings_type::AccurateSpuDMA, { "Core", "Accurate SPU DMA"}}, diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index f153696617..3628fb33f0 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -1452,8 +1452,8 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std m_emu_settings->EnhanceCheckBox(ui->fixupPPUVNAN, emu_settings_type::FixupPPUVNAN); SubscribeTooltip(ui->fixupPPUVNAN, tooltips.settings.fixup_ppuvnan); - m_emu_settings->EnhanceCheckBox(ui->ppuPrecompilation, emu_settings_type::PPULLVMPrecompilation); - SubscribeTooltip(ui->ppuPrecompilation, tooltips.settings.ppu_precompilation); + m_emu_settings->EnhanceCheckBox(ui->llvmPrecompilation, emu_settings_type::LLVMPrecompilation); + SubscribeTooltip(ui->llvmPrecompilation, tooltips.settings.llvm_precompilation); m_emu_settings->EnhanceCheckBox(ui->suspendSavestates, emu_settings_type::SuspendEmulationSavestateMode); SubscribeTooltip(ui->suspendSavestates, tooltips.settings.suspend_savestates); diff --git a/rpcs3/rpcs3qt/settings_dialog.ui b/rpcs3/rpcs3qt/settings_dialog.ui index dd9c67dd7a..bafa455676 100644 --- a/rpcs3/rpcs3qt/settings_dialog.ui +++ b/rpcs3/rpcs3qt/settings_dialog.ui @@ -2394,9 +2394,9 @@
- + - PPU LLVM Precompilation + PPU/SPU LLVM Precompilation diff --git a/rpcs3/rpcs3qt/tooltips.h b/rpcs3/rpcs3qt/tooltips.h index 1982a108e0..6e73f2f4b2 100644 --- a/rpcs3/rpcs3qt/tooltips.h +++ b/rpcs3/rpcs3qt/tooltips.h @@ -75,7 +75,7 @@ public: const QString ppu__static = tr("Interpreter (slow). Try this if PPU Recompiler (LLVM) doesn't work."); const QString ppu_dynamic = tr("Alternative interpreter (slow). May be faster than static interpreter. Try this if PPU Recompiler (LLVM) doesn't work."); const QString ppu_llvm = tr("Recompiles and caches the game's PPU code using the LLVM Recompiler once before running it for the first time.\nThis is by far the fastest option and should always be used.\nShould you face compatibility issues, fall back to one of the Interpreters and retry.\nIf unsure, use this option."); - const QString ppu_precompilation = tr("Searches the game's directory and precompiles extra PPU modules during boot.\nIf disabled, these modules will only be compiled when needed. Depending on the game, this might interrupt the gameplay unexpectedly and possibly frequently.\nOnly disable this if you want to get ingame more quickly."); + const QString llvm_precompilation = tr("Searches the game's directory and precompiles extra PPU and SPU modules during boot.\nIf disabled, these modules will only be compiled when needed. Depending on the game, this might interrupt the gameplay unexpectedly and possibly frequently.\nOnly disable this if you want to get ingame more quickly."); const QString spu__static = tr("Interpreter (slow). Try this if SPU Recompiler (LLVM) doesn't work."); const QString spu_dynamic = tr("Alternative interpreter (slow). May be faster than static interpreter. Try this if SPU Recompiler (LLVM) doesn't work."); const QString spu_asmjit = tr("Recompiles the game's SPU code using the ASMJIT Recompiler.\nThis is the fast option with very good compatibility.\nIf unsure, use this option."); From 3d2229ca0577066fc7cd45c15a742d05551c7f42 Mon Sep 17 00:00:00 2001 From: Elad Ashkenazi Date: Mon, 28 Aug 2023 12:20:17 +0300 Subject: [PATCH 139/184] SPU LLVM Precompilation Fixup --- rpcs3/Emu/Cell/SPURecompiler.cpp | 39 ++++++++++++++------------------ rpcs3/Emu/Cell/SPUThread.cpp | 14 ------------ 2 files changed, 17 insertions(+), 36 deletions(-) diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index acef8a2747..3b863dd589 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -699,7 +699,19 @@ void spu_cache::initialize() auto data_list = std::move(g_fxo->get().data); g_fxo->get().had_been_used = true; - atomic_t data_indexer{}; + const bool spu_precompilation_enabled = func_list.empty() && g_cfg.core.spu_cache && g_cfg.core.llvm_precompilation; + + if (spu_precompilation_enabled) + { + // What compiles in this case goes straight to disk + g_fxo->get() = std::move(cache); + } + else + { + data_list.clear(); + } + + atomic_t data_indexer = 0; if (g_cfg.core.spu_decoder == spu_decoder_type::dynamic || g_cfg.core.spu_decoder == spu_decoder_type::llvm) { @@ -742,12 +754,9 @@ void spu_cache::initialize() u32 add_count = ::size32(func_list); - if (func_list.empty()) + for (auto& sec : data_list) { - for (auto& sec : data_list) - { - add_count += sec.funcs.size(); - } + add_count += sec.funcs.size(); } g_progr_ptotal += add_count; @@ -847,12 +856,6 @@ void spu_cache::initialize() result++; } - if (!func_list.empty() || !g_cfg.core.llvm_precompilation) - { - // Cache has already been initiated or the user does not want to precompile SPU programs - return result; - } - u32 last_sec_idx = umax; for (usz func_i = data_indexer++;; func_i = data_indexer++, g_progr_pdone++) @@ -1077,7 +1080,7 @@ void spu_cache::initialize() } // Initialize global cache instance - if (g_cfg.core.spu_cache) + if (g_cfg.core.spu_cache && cache) { g_fxo->get() = std::move(cache); } @@ -2900,8 +2903,6 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point) } } - spu_program result2 = result; - while (lsa > 0 || limit < 0x40000) { const u32 initial_size = ::size32(result.data); @@ -3348,13 +3349,7 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point) { workload.clear(); workload.push_back(entry_point); - if (!m_bbs.count(entry_point)) - { - std::string func_bad; - dump(result2, func_bad); - spu_log.error("%s", func_bad); - return {}; - } + ensure(m_bbs.count(entry_point)); std::basic_string new_entries; diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 9376663893..e5648f9cd0 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -487,20 +487,6 @@ std::array op_branch_targets(u32 pc, spu_opcode_t op) case spu_itype::BRASL: { const int index = (type == spu_itype::BR || type == spu_itype::BRA || type == spu_itype::BRSL || type == spu_itype::BRASL ? 0 : 1); - - // if (type == spu_itype::BRASL || type == spu_itype::BRA) - // { - // res[index] = spu_branch_target(0, op.i16); - // } - // else - // { - // // Treat i16 as signed, this allows the caller to detect "overflows" and "underflows" in address in order to detect invalid branches - // // Example: - // // [0x3fffc] BR +4 -> BR 0 -> invalid - // // [0x3fffc] BR 0x3fff4 -> BR 0 -> invalid - // const u32 add = static_cast(op.si16); - // } - res[index] = (spu_branch_target(type == spu_itype::BRASL || type == spu_itype::BRA ? 0 : pc, op.i16)); if (res[0] == res[1]) From 4acd8194789e7f9ddf57e1174e34c8c79c042a0f Mon Sep 17 00:00:00 2001 From: Ivan Chikish Date: Mon, 28 Aug 2023 21:53:02 +0300 Subject: [PATCH 140/184] [DS4] Proper USB/BT detection --- rpcs3/Input/ds4_pad_handler.cpp | 40 +++++++-------------------- rpcs3/Input/dualsense_pad_handler.cpp | 1 + 2 files changed, 11 insertions(+), 30 deletions(-) diff --git a/rpcs3/Input/ds4_pad_handler.cpp b/rpcs3/Input/ds4_pad_handler.cpp index 45007ff4a7..0f8c962bd0 100644 --- a/rpcs3/Input/ds4_pad_handler.cpp +++ b/rpcs3/Input/ds4_pad_handler.cpp @@ -514,38 +514,18 @@ void ds4_pad_handler::check_add_device(hid_device* hidDevice, std::string_view p } std::string serial; + for (wchar_t ch : wide_serial) + serial += static_cast(ch); - // There isnt a nice 'portable' way with hidapi to detect bt vs wired as the pid/vid's are the same - // Let's try getting 0x81 feature report, which should return the mac address on wired, and should an error on bluetooth - std::array buf{}; - buf[0] = 0x81; - int res = hid_get_feature_report(hidDevice, buf.data(), DS4_FEATURE_REPORT_0x81_SIZE); - if (res > 0) + const hid_device_info* devinfo = hid_get_device_info(hidDevice); + if (!devinfo) { - if (res != DS4_FEATURE_REPORT_0x81_SIZE || buf[0] != 0x81) - { - // Controller may not be genuine. These controllers do not have feature 0x81 implemented and calibration data is in bluetooth format even in USB mode! - ds4_log.warning("check_add_device: DS4 controller may not be genuine. Workaround enabled. (result=%d, buf[0]=0x%x)", res, buf[0]); - - // Read feature report 0x12 instead which is what the console uses. - buf = {}; - buf[0] = 0x12; - if (res = hid_get_feature_report(hidDevice, buf.data(), DS4_FEATURE_REPORT_0x12_SIZE); res != DS4_FEATURE_REPORT_0x12_SIZE || buf[0] != 0x12) - { - ds4_log.error("check_add_device: hid_get_feature_report 0x12 failed! result=%d, buf[0]=0x%x, error=%s", res, buf[0], hid_error(hidDevice)); - } - } - - serial = fmt::format("%x%x%x%x%x%x", buf[6], buf[5], buf[4], buf[3], buf[2], buf[1]); - } - else - { - ds4_log.warning("check_add_device: DS4 Bluetooth controller detected. (hid_get_feature_report 0x81 failed, result=%d, buf[0]=0x%x, error=%s)", res, buf[0], hid_error(hidDevice)); - device->bt_controller = true; - for (wchar_t ch : wide_serial) - serial += static_cast(ch); + ds4_log.error("check_add_device: hid_get_device_info failed! error=%s", hid_error(hidDevice)); + hid_close(hidDevice); + return; } + device->bt_controller = (devinfo->bus_type == HID_API_BUS_BLUETOOTH); device->hidDevice = hidDevice; if (!GetCalibrationData(device)) @@ -559,10 +539,10 @@ void ds4_pad_handler::check_add_device(hid_device* hidDevice, std::string_view p u32 hw_version{}; u32 fw_version{}; - buf = {}; + std::array buf{}; buf[0] = 0xA3; - res = hid_get_feature_report(hidDevice, buf.data(), DS4_FEATURE_REPORT_0xA3_SIZE); + int res = hid_get_feature_report(hidDevice, buf.data(), DS4_FEATURE_REPORT_0xA3_SIZE); if (res != DS4_FEATURE_REPORT_0xA3_SIZE || buf[0] != 0xA3) { ds4_log.error("check_add_device: hid_get_feature_report 0xA3 failed! Could not retrieve firmware version! result=%d, buf[0]=0x%x, error=%s", res, buf[0], hid_error(hidDevice)); diff --git a/rpcs3/Input/dualsense_pad_handler.cpp b/rpcs3/Input/dualsense_pad_handler.cpp index e9e2627a3c..f8cfac26af 100644 --- a/rpcs3/Input/dualsense_pad_handler.cpp +++ b/rpcs3/Input/dualsense_pad_handler.cpp @@ -195,6 +195,7 @@ void dualsense_pad_handler::check_add_device(hid_device* hidDevice, std::string_ if (res < 0 || buf[0] != 0x09) { dualsense_log.error("check_add_device: hid_get_feature_report 0x09 failed! result=%d, buf[0]=0x%x, error=%s", res, buf[0], hid_error(hidDevice)); + hid_close(hidDevice); return; } From 4c7aee75c19409200ce626d0df7bda5f764650d2 Mon Sep 17 00:00:00 2001 From: shinra-electric <50119606+shinra-electric@users.noreply.github.com> Date: Sun, 27 Aug 2023 11:36:11 +0200 Subject: [PATCH 141/184] Update homebrew mvk on build script MVK was updated without a new tag. This updates to the later release. --- .ci/build-mac.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/build-mac.sh b/.ci/build-mac.sh index a1467aca59..0880f2d28a 100755 --- a/.ci/build-mac.sh +++ b/.ci/build-mac.sh @@ -10,7 +10,7 @@ arch -x86_64 /usr/local/bin/brew install -f --overwrite llvm@16 glew cmake sdl2 arch -x86_64 /usr/local/bin/brew link -f llvm@16 # moltenvk based on commit for 1.2.5 release -wget https://raw.githubusercontent.com/Homebrew/homebrew-core/055ae78415b61ecf1fa3de32b76b8a149855f903/Formula/m/molten-vk.rb +wget https://raw.githubusercontent.com/Homebrew/homebrew-core/b0bba05b617ef0fd796b3727be46addfd098a491/Formula/m/molten-vk.rb arch -x86_64 /usr/local/bin/brew install -f --overwrite ./molten-vk.rb #export MACOSX_DEPLOYMENT_TARGET=12.0 export CXX=clang++ From d105b0bd9f7b9eb73d54a5c5dc045789e863e3ec Mon Sep 17 00:00:00 2001 From: shinra-electric <50119606+shinra-electric@users.noreply.github.com> Date: Sun, 27 Aug 2023 11:38:30 +0200 Subject: [PATCH 142/184] Update MVK MVK 1.2.5 was updated without changing the tag. This uses the latest version, --- 3rdparty/MoltenVK/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/MoltenVK/CMakeLists.txt b/3rdparty/MoltenVK/CMakeLists.txt index a673db6e11..1b799ae0b0 100644 --- a/3rdparty/MoltenVK/CMakeLists.txt +++ b/3rdparty/MoltenVK/CMakeLists.txt @@ -4,7 +4,7 @@ include(ExternalProject) ExternalProject_Add(moltenvk GIT_REPOSITORY https://github.com/KhronosGroup/MoltenVK.git - GIT_TAG b3c9f86 + GIT_TAG 02a8c01 BUILD_IN_SOURCE 1 SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK CONFIGURE_COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/fetchDependencies" --macos From 452a4654dac4cdf3dba4d502783e82257d9d40a1 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 26 Aug 2023 12:24:47 +0200 Subject: [PATCH 143/184] input: add SDL_GameControllerDB --- .ci/deploy-windows.sh | 5 ++++- .gitmodules | 1 + rpcs3/Emu/system_config.h | 1 + rpcs3/Input/sdl_pad_handler.cpp | 20 ++++++++++++++++++++ rpcs3/rpcs3qt/emu_settings_type.h | 2 ++ rpcs3/rpcs3qt/settings_dialog.cpp | 7 +++++++ rpcs3/rpcs3qt/settings_dialog.ui | 7 +++++++ rpcs3/rpcs3qt/tooltips.h | 1 + 8 files changed, 43 insertions(+), 1 deletion(-) diff --git a/.ci/deploy-windows.sh b/.ci/deploy-windows.sh index 417e8e8a1b..dd54ec4649 100755 --- a/.ci/deploy-windows.sh +++ b/.ci/deploy-windows.sh @@ -7,8 +7,11 @@ ARTIFACT_DIR="$BUILD_ARTIFACTSTAGINGDIRECTORY" rm -f ./bin/rpcs3.exp ./bin/rpcs3.lib ./bin/rpcs3.pdb ./bin/vc_redist.x64.exe rm -rf ./bin/git -# Prepare compatibility database for packaging, as well as +# Prepare compatibility and SDL database for packaging, as well as # certificate for ssl (auto-updater) +mkdir ./bin/config +mkdir ./bin/config/input_configs +curl -fsSL 'https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt' 1> ./bin/config/input_configs/gamecontrollerdb.txt curl -fsSL 'https://rpcs3.net/compatibility?api=v1&export' | iconv -t UTF-8 1> ./bin/GuiConfigs/compat_database.dat curl -fsSL 'https://curl.haxx.se/ca/cacert.pem' 1> ./bin/cacert.pem diff --git a/.gitmodules b/.gitmodules index b9aa5ccf26..cf1e922050 100644 --- a/.gitmodules +++ b/.gitmodules @@ -87,3 +87,4 @@ [submodule "3rdparty/rtmidi/rtmidi"] path = 3rdparty/rtmidi/rtmidi url = ../../thestk/rtmidi + ignore = dirty diff --git a/rpcs3/Emu/system_config.h b/rpcs3/Emu/system_config.h index 6d6ef869e7..7964a6b195 100644 --- a/rpcs3/Emu/system_config.h +++ b/rpcs3/Emu/system_config.h @@ -278,6 +278,7 @@ struct cfg_root : cfg::node cfg::_bool show_move_cursor{this, "Show move cursor", false, true}; cfg::_bool lock_overlay_input_to_player_one{this, "Lock overlay input to player one", false, true}; cfg::string midi_devices{ this, "Emulated Midi devices", "ßßß@@@ßßß@@@ßßß@@@" }; + cfg::_bool load_sdl_mappings{ this, "Load SDL GameController Mappings", true }; } io{ this }; struct node_sys : cfg::node diff --git a/rpcs3/Input/sdl_pad_handler.cpp b/rpcs3/Input/sdl_pad_handler.cpp index 925eeb7f29..f25ab21125 100644 --- a/rpcs3/Input/sdl_pad_handler.cpp +++ b/rpcs3/Input/sdl_pad_handler.cpp @@ -2,6 +2,8 @@ #include "stdafx.h" #include "sdl_pad_handler.h" +#include "Emu/system_utils.hpp" +#include "Emu/system_config.h" LOG_CHANNEL(sdl_log, "SDL"); @@ -217,6 +219,24 @@ bool sdl_pad_handler::Init() } }, nullptr); + if (g_cfg.io.load_sdl_mappings) + { + const std::string db_path = rpcs3::utils::get_input_config_root() + "gamecontrollerdb.txt"; + sdl_log.notice("Adding mappings from file '%s'", db_path); + + if (fs::is_file(db_path)) + { + if (SDL_GameControllerAddMappingsFromFile(db_path.c_str()) < 0) + { + sdl_log.error("Could not add mappings from file '%s'! SDL Error: %s", db_path, SDL_GetError()); + } + } + else + { + sdl_log.error("Could not add mappings from file '%s'! File does not exist!", db_path); + } + } + m_is_init = true; enumerate_devices(); diff --git a/rpcs3/rpcs3qt/emu_settings_type.h b/rpcs3/rpcs3qt/emu_settings_type.h index a3371d57d9..edf9d6cc8e 100644 --- a/rpcs3/rpcs3qt/emu_settings_type.h +++ b/rpcs3/rpcs3qt/emu_settings_type.h @@ -157,6 +157,7 @@ enum class emu_settings_type Turntable, GHLtar, MidiDevices, + SDLMappings, // Misc ExitRPCS3OnFinish, @@ -341,6 +342,7 @@ inline static const QMap settings_location = { emu_settings_type::Turntable, { "Input/Output", "Turntable emulated controller" }}, { emu_settings_type::GHLtar, { "Input/Output", "GHLtar emulated controller" }}, { emu_settings_type::MidiDevices, { "Input/Output", "Emulated Midi devices" }}, + { emu_settings_type::SDLMappings, { "Input/Output", "Load SDL GameController Mappings" }}, // Misc { emu_settings_type::ExitRPCS3OnFinish, { "Miscellaneous", "Exit RPCS3 when process finishes" }}, diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index 3628fb33f0..6936211946 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -1226,6 +1226,13 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std m_emu_settings->EnhanceCheckBox(ui->lockOverlayInputToPlayerOne, emu_settings_type::LockOvlIptToP1); SubscribeTooltip(ui->lockOverlayInputToPlayerOne, tooltips.settings.lock_overlay_input_to_player_one); +#if HAVE_SDL2 + m_emu_settings->EnhanceCheckBox(ui->loadSdlMappings, emu_settings_type::SDLMappings); + SubscribeTooltip(ui->loadSdlMappings, tooltips.settings.sdl_mappings); +#else + ui->loadSdlMappings->setVisible(false); +#endif + // Midi const QString midi_none = m_emu_settings->m_midi_creator.get_none(); const midi_device def_midi_device{ .type = midi_device_type::keyboard, .name = midi_none.toStdString() }; diff --git a/rpcs3/rpcs3qt/settings_dialog.ui b/rpcs3/rpcs3qt/settings_dialog.ui index bafa455676..152cfda615 100644 --- a/rpcs3/rpcs3qt/settings_dialog.ui +++ b/rpcs3/rpcs3qt/settings_dialog.ui @@ -1810,6 +1810,13 @@
+ + + + Use SDL GameController Database + + + diff --git a/rpcs3/rpcs3qt/tooltips.h b/rpcs3/rpcs3qt/tooltips.h index 6e73f2f4b2..ce20096ec9 100644 --- a/rpcs3/rpcs3qt/tooltips.h +++ b/rpcs3/rpcs3qt/tooltips.h @@ -227,6 +227,7 @@ public: const QString background_input = tr("Allows pad and keyboard input while the game window is unfocused."); const QString show_move_cursor = tr("Shows the raw position of the PS Move input.\nThis can be very helpful during calibration screens."); const QString midi_devices = tr("Select up to 3 emulated MIDI devices and their types."); + const QString sdl_mappings = tr("Loads the SDL GameController database for improved gamepad compatibility. Only used in the SDL pad handler."); const QString lock_overlay_input_to_player_one = tr("Locks the native overlay input to the first player."); From 565a208f2027f7e5c67bf0648cd835f7fd4fa7e0 Mon Sep 17 00:00:00 2001 From: nastys <7950891+nastys@users.noreply.github.com> Date: Tue, 29 Aug 2023 04:18:36 +0200 Subject: [PATCH 144/184] macOS CI: build ffmpeg and gnutls from source, and fix deploy script (#14563) --- .ci/build-mac.sh | 7 ++++--- .ci/deploy-mac.sh | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.ci/build-mac.sh b/.ci/build-mac.sh index 0880f2d28a..e5fce3877f 100755 --- a/.ci/build-mac.sh +++ b/.ci/build-mac.sh @@ -6,7 +6,8 @@ brew install -f --overwrite nasm ninja git p7zip create-dmg ccache pipenv #/usr/sbin/softwareupdate --install-rosetta --agree-to-license arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" arch -x86_64 /usr/local/bin/brew update -arch -x86_64 /usr/local/bin/brew install -f --overwrite llvm@16 glew cmake sdl2 vulkan-headers ffmpeg +arch -x86_64 /usr/local/bin/brew install --build-from-source ffmpeg gnutls +arch -x86_64 /usr/local/bin/brew install -f --overwrite llvm@16 glew cmake sdl2 vulkan-headers coreutils arch -x86_64 /usr/local/bin/brew link -f llvm@16 # moltenvk based on commit for 1.2.5 release @@ -58,9 +59,9 @@ export VK_ICD_FILENAMES="$VULKAN_SDK/share/vulkan/icd.d/MoltenVK_icd.json" export LLVM_DIR LLVM_DIR="BREW_X64_PATH/opt/llvm@16" -# exclude FAudio, SPIRV and LLVM, and sdl from submodule update +# exclude ffmpeg, SPIRV and LLVM, and sdl from submodule update # shellcheck disable=SC2046 -git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/llvm/ && !/SPIRV/ && !/SDL/ { print $3 }' .gitmodules) +git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/ffmpeg/ && !/llvm/ && !/SPIRV/ && !/SDL/ { print $3 }' .gitmodules) # 3rdparty fixes sed -i '' "s/extern const double NSAppKitVersionNumber;/const double NSAppKitVersionNumber = 1343;/g" 3rdparty/hidapi/hidapi/mac/hid.c diff --git a/.ci/deploy-mac.sh b/.ci/deploy-mac.sh index 8477ae6fac..286aed42f3 100755 --- a/.ci/deploy-mac.sh +++ b/.ci/deploy-mac.sh @@ -16,6 +16,8 @@ cd bin mkdir "rpcs3.app/Contents/lib/" cp "/usr/local/opt/llvm@16/lib/c++/libc++abi.1.0.dylib" "rpcs3.app/Contents/lib/libc++abi.1.dylib" +cp "$(realpath /usr/local/lib/libsharpyuv.0.dylib)" "rpcs3.app/Contents/lib/libsharpyuv.0.dylib" +cp "$(realpath /usr/local/lib/libintl.8.dylib)" "rpcs3.app/Contents/lib/libintl.8.dylib" rm -rf "rpcs3.app/Contents/Frameworks/QtPdf.framework" \ "rpcs3.app/Contents/Frameworks/QtQml.framework" \ From 7144e92ce2ca8aea17f8661ff7c33e920c5697fd Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 29 Aug 2023 20:46:09 +0200 Subject: [PATCH 145/184] Qt: fix pressure intensity deadzone enabled state --- rpcs3/Emu/Io/PadHandler.cpp | 2 +- rpcs3/Input/dualsense_pad_handler.cpp | 12 +++++------- rpcs3/Input/keyboard_pad_handler.cpp | 2 +- rpcs3/Input/keyboard_pad_handler.h | 6 +++--- rpcs3/rpcs3qt/pad_settings_dialog.cpp | 1 + 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/rpcs3/Emu/Io/PadHandler.cpp b/rpcs3/Emu/Io/PadHandler.cpp index 5cf84db6f9..665bee4b63 100644 --- a/rpcs3/Emu/Io/PadHandler.cpp +++ b/rpcs3/Emu/Io/PadHandler.cpp @@ -623,7 +623,7 @@ void PadHandlerBase::get_mapping(const pad_ensemble& binding) s32 stick_val[4]{}; // Translate any corresponding keycodes to our two sticks. (ignoring thresholds for now) - for (int i = 0; i < static_cast(pad->m_sticks.size()); i++) + for (usz i = 0; i < pad->m_sticks.size(); i++) { bool pressed{}; u16 val_min{}; diff --git a/rpcs3/Input/dualsense_pad_handler.cpp b/rpcs3/Input/dualsense_pad_handler.cpp index f8cfac26af..80712f9021 100644 --- a/rpcs3/Input/dualsense_pad_handler.cpp +++ b/rpcs3/Input/dualsense_pad_handler.cpp @@ -956,14 +956,12 @@ int dualsense_pad_handler::send_output_report(DualSenseDevice* device) return hid_write(device->hidDevice, &report.report_id, DUALSENSE_BLUETOOTH_REPORT_SIZE); } - else - { - output_report_usb report{}; - report.report_id = 0x02; // report id for usb - report.common = common; - return hid_write(device->hidDevice, &report.report_id, DUALSENSE_USB_REPORT_SIZE); - } + output_report_usb report{}; + report.report_id = 0x02; // report id for usb + report.common = common; + + return hid_write(device->hidDevice, &report.report_id, DUALSENSE_USB_REPORT_SIZE); } void dualsense_pad_handler::apply_pad_data(const pad_ensemble& binding) diff --git a/rpcs3/Input/keyboard_pad_handler.cpp b/rpcs3/Input/keyboard_pad_handler.cpp index a341b50532..283605bd31 100644 --- a/rpcs3/Input/keyboard_pad_handler.cpp +++ b/rpcs3/Input/keyboard_pad_handler.cpp @@ -1062,7 +1062,7 @@ void keyboard_pad_handler::process() { if (update_sticks) { - for (int j = 0; j < static_cast(pad.m_sticks.size()); j++) + for (usz j = 0; j < pad.m_sticks.size(); j++) { const f32 stick_lerp_factor = (j < 2) ? m_l_stick_lerp_factor : m_r_stick_lerp_factor; diff --git a/rpcs3/Input/keyboard_pad_handler.h b/rpcs3/Input/keyboard_pad_handler.h index 60ea9c57f6..3fa69286e2 100644 --- a/rpcs3/Input/keyboard_pad_handler.h +++ b/rpcs3/Input/keyboard_pad_handler.h @@ -125,9 +125,9 @@ private: f32 m_r_stick_lerp_factor = 1.0f; u32 m_l_stick_multiplier = 100; u32 m_r_stick_multiplier = 100; - u8 m_stick_min[4] = { 0, 0, 0, 0 }; - u8 m_stick_max[4] = { 128, 128, 128, 128 }; - u8 m_stick_val[4] = { 128, 128, 128, 128 }; + std::array m_stick_min{ 0, 0, 0, 0 }; + std::array m_stick_max{ 128, 128, 128, 128 }; + std::array m_stick_val{ 128, 128, 128, 128 }; // Mouse Movements steady_clock::time_point m_last_mouse_move_left; diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.cpp b/rpcs3/rpcs3qt/pad_settings_dialog.cpp index 9aa5afe430..b88075576e 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/pad_settings_dialog.cpp @@ -1221,6 +1221,7 @@ void pad_settings_dialog::SwitchButtons(bool is_enabled) ui->kb_stick_multi_right->setEnabled(is_enabled); ui->squircle_left->setEnabled(is_enabled); ui->squircle_right->setEnabled(is_enabled); + ui->gb_pressure_intensity_deadzone->setEnabled(is_enabled); ui->gb_pressure_intensity->setEnabled(is_enabled && m_enable_pressure_intensity_button); ui->gb_vibration->setEnabled(is_enabled && m_enable_rumble); ui->gb_motion_controls->setEnabled(is_enabled && m_enable_motion); From 105c5759f35e47d3f9db97227c4a3ebd42e7c3de Mon Sep 17 00:00:00 2001 From: Elad Ashkenazi Date: Mon, 28 Aug 2023 15:40:18 +0300 Subject: [PATCH 146/184] Add SPU Precompilation to Create PPU Cache --- rpcs3/Emu/Cell/PPUThread.cpp | 9 ++++++++- rpcs3/Emu/Cell/SPURecompiler.cpp | 26 +++++++++++++++++--------- rpcs3/Emu/Cell/SPURecompiler.h | 2 +- rpcs3/Emu/System.cpp | 12 ++++++++++++ rpcs3/rpcs3qt/game_list_frame.cpp | 21 +++++++++++---------- rpcs3/rpcs3qt/game_list_frame.h | 6 +++--- rpcs3/rpcs3qt/main_window.cpp | 2 +- rpcs3/rpcs3qt/main_window.ui | 6 +++--- 8 files changed, 56 insertions(+), 28 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index a64f59ca88..2602afdfcd 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -2112,7 +2112,14 @@ void ppu_thread::cpu_task() #endif cmd_pop(); - ppu_initialize(), spu_cache::initialize(); + ppu_initialize(); + + if (Emu.IsStopped()) + { + return; + } + + spu_cache::initialize(); #ifdef __APPLE__ pthread_jit_write_protect_np(true); diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index 3b863dd589..45e08df319 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -661,7 +661,7 @@ void spu_cache::add(const spu_program& func) m_file.write_gather(gather, 3); } -void spu_cache::initialize() +void spu_cache::initialize(bool build_existing_cache) { spu_runtime::g_interpreter = spu_runtime::g_gateway; @@ -699,13 +699,24 @@ void spu_cache::initialize() auto data_list = std::move(g_fxo->get().data); g_fxo->get().had_been_used = true; - const bool spu_precompilation_enabled = func_list.empty() && g_cfg.core.spu_cache && g_cfg.core.llvm_precompilation; + u32 total_precompile = 0; + + for (auto& sec : data_list) + { + total_precompile += sec.funcs.size(); + } + + const bool spu_precompilation_enabled = (build_existing_cache ? func_list.empty() : func_list.size() < total_precompile) && g_cfg.core.spu_cache && g_cfg.core.llvm_precompilation; if (spu_precompilation_enabled) { // What compiles in this case goes straight to disk g_fxo->get() = std::move(cache); } + else if (!build_existing_cache) + { + return; + } else { data_list.clear(); @@ -752,17 +763,14 @@ void spu_cache::initialize() thread_ctrl::wait_on(g_progr_ptotal, v); } - u32 add_count = ::size32(func_list); + const u32 add_count = ::size32(func_list) + total_precompile; - for (auto& sec : data_list) + if (add_count) { - add_count += sec.funcs.size(); + g_progr_ptotal += add_count; + progr.emplace("Building SPU cache..."); } - g_progr_ptotal += add_count; - - progr.emplace("Building SPU cache..."); - worker_count = rpcs3::utils::get_max_threads(); } diff --git a/rpcs3/Emu/Cell/SPURecompiler.h b/rpcs3/Emu/Cell/SPURecompiler.h index 551e73a13a..5dd68184b1 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.h +++ b/rpcs3/Emu/Cell/SPURecompiler.h @@ -34,7 +34,7 @@ public: void add(const struct spu_program& func); - static void initialize(); + static void initialize(bool build_existing_cache = true); }; struct spu_program diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index e85cc6a6cf..cba2644511 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -15,6 +15,7 @@ #include "Emu/Cell/PPUDisAsm.h" #include "Emu/Cell/PPUAnalyser.h" #include "Emu/Cell/SPUThread.h" +#include "Emu/Cell/SPURecompiler.h" #include "Emu/RSX/RSXThread.h" #include "Emu/Cell/lv2/sys_process.h" #include "Emu/Cell/lv2/sys_sync.h" @@ -1385,6 +1386,10 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, // Force LLVM recompiler g_cfg.core.ppu_decoder.from_default(); + // Force SPU cache and precompilation + g_cfg.core.llvm_precompilation.set(true); + g_cfg.core.spu_cache.set(true); + // Disable incompatible settings fixup_ppu_settings(); @@ -1490,6 +1495,13 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, ppu_precompile(dir_queue, nullptr); + if (Emu.IsStopped()) + { + return; + } + + spu_cache::initialize(false); + // Exit "process" CallFromMainThread([this] { diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index b8e66edadb..28181afba4 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -1101,7 +1101,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) ? tr("&Change Custom Gamepad Configuration") : tr("&Create Custom Gamepad Configuration")); QAction* configure_patches = menu.addAction(tr("&Manage Game Patches")); - QAction* create_ppu_cache = menu.addAction(tr("&Create PPU Cache")); + QAction* create_ppu_cache = menu.addAction(tr("&Create PPU/SPU Cache")); menu.addSeparator(); @@ -1440,7 +1440,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) { if (m_gui_settings->GetBootConfirmation(this)) { - CreatePPUCache(gameinfo); + CreateCPUCaches(gameinfo); } }); connect(remove_game, &QAction::triggered, this, [this, current_game, gameinfo, cache_base_dir, name] @@ -1612,23 +1612,24 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) menu.exec(global_pos); } -bool game_list_frame::CreatePPUCache(const std::string& path, const std::string& serial) +bool game_list_frame::CreateCPUCaches(const std::string& path, const std::string& serial) { Emu.GracefulShutdown(false); Emu.SetForceBoot(true); if (const auto error = Emu.BootGame(fs::is_file(path) ? fs::get_parent_dir(path) : path, serial, true); error != game_boot_result::no_errors) { - game_list_log.error("Could not create PPU Cache for %s, error: %s", path, error); + game_list_log.error("Could not create PPU and SPU caches for %s, error: %s", path, error); return false; } - game_list_log.warning("Creating PPU Cache for %s", path); + + game_list_log.warning("Creating PPU/SPU Caches for %s", path); return true; } -bool game_list_frame::CreatePPUCache(const game_info& game) +bool game_list_frame::CreateCPUCaches(const game_info& game) { - return game && CreatePPUCache(game->info.path, game->info.serial); + return game && CreateCPUCaches(game->info.path, game->info.serial); } bool game_list_frame::RemoveCustomConfiguration(const std::string& title_id, const game_info& game, bool is_interactive) @@ -1907,7 +1908,7 @@ void game_list_frame::RemoveHDD1Cache(const std::string& base_dir, const std::st game_list_log.fatal("Only %d/%d HDD1 cache directories could be removed in %s (%s)", dirs_removed, dirs_total, base_dir, title_id); } -void game_list_frame::BatchCreatePPUCaches() +void game_list_frame::BatchCreateCPUCaches() { const std::string vsh_path = g_cfg_vfs.get_dev_flash() + "vsh/module/"; const bool vsh_exists = fs::is_file(vsh_path + "vsh.self"); @@ -1952,7 +1953,7 @@ void game_list_frame::BatchCreatePPUCaches() pdlg->setLabelText(tr("%0\nProgress: %1/%2. Compiling caches for VSH...", "Second line after main label").arg(main_label).arg(created).arg(total)); QApplication::processEvents(); - if (CreatePPUCache(vsh_path) && wait_until_compiled()) + if (CreateCPUCaches(vsh_path) && wait_until_compiled()) { pdlg->SetValue(++created); } @@ -1968,7 +1969,7 @@ void game_list_frame::BatchCreatePPUCaches() pdlg->setLabelText(tr("%0\nProgress: %1/%2. Compiling caches for %3...", "Second line after main label").arg(main_label).arg(created).arg(total).arg(qstr(game->info.serial))); QApplication::processEvents(); - if (CreatePPUCache(game) && wait_until_compiled()) + if (CreateCPUCaches(game) && wait_until_compiled()) { pdlg->SetValue(++created); } diff --git a/rpcs3/rpcs3qt/game_list_frame.h b/rpcs3/rpcs3qt/game_list_frame.h index 582d407a52..ecc7e106c7 100644 --- a/rpcs3/rpcs3qt/game_list_frame.h +++ b/rpcs3/rpcs3qt/game_list_frame.h @@ -63,7 +63,7 @@ public: bool IsEntryVisible(const game_info& game, bool search_fallback = false) const; public Q_SLOTS: - void BatchCreatePPUCaches(); + void BatchCreateCPUCaches(); void BatchRemovePPUCaches(); void BatchRemoveSPUCaches(); void BatchRemoveCustomConfigurations(); @@ -108,8 +108,8 @@ private: bool RemovePPUCache(const std::string& base_dir, bool is_interactive = false); bool RemoveSPUCache(const std::string& base_dir, bool is_interactive = false); void RemoveHDD1Cache(const std::string& base_dir, const std::string& title_id, bool is_interactive = false); - static bool CreatePPUCache(const std::string& path, const std::string& serial = {}); - static bool CreatePPUCache(const game_info& game); + static bool CreateCPUCaches(const std::string& path, const std::string& serial = {}); + static bool CreateCPUCaches(const game_info& game); static std::string GetCacheDirBySerial(const std::string& serial); static std::string GetDataDirBySerial(const std::string& serial); diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index a99fec89f8..2313e9f9b6 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -2506,7 +2506,7 @@ void main_window::CreateConnects() }); connect(ui->exitAct, &QAction::triggered, this, &QWidget::close); - connect(ui->batchCreatePPUCachesAct, &QAction::triggered, m_game_list_frame, &game_list_frame::BatchCreatePPUCaches); + connect(ui->batchCreateCPUCachesAct, &QAction::triggered, m_game_list_frame, &game_list_frame::BatchCreateCPUCaches); connect(ui->batchRemovePPUCachesAct, &QAction::triggered, m_game_list_frame, &game_list_frame::BatchRemovePPUCaches); connect(ui->batchRemoveSPUCachesAct, &QAction::triggered, m_game_list_frame, &game_list_frame::BatchRemoveSPUCaches); connect(ui->batchRemoveShaderCachesAct, &QAction::triggered, m_game_list_frame, &game_list_frame::BatchRemoveShaderCaches); diff --git a/rpcs3/rpcs3qt/main_window.ui b/rpcs3/rpcs3qt/main_window.ui index 9c93bf5c79..35422d94fc 100644 --- a/rpcs3/rpcs3qt/main_window.ui +++ b/rpcs3/rpcs3qt/main_window.ui @@ -182,7 +182,7 @@ All Titles - + @@ -1094,9 +1094,9 @@ Show Title Bars - + - Create PPU Caches + Create PPU/SPU Caches From 37212a632c3f9389449ed3e8462847f31ce8c955 Mon Sep 17 00:00:00 2001 From: Eladash Date: Tue, 29 Aug 2023 14:50:50 +0300 Subject: [PATCH 147/184] SPU: Refactor function discovery --- rpcs3/Emu/Cell/RawSPUThread.cpp | 2 +- rpcs3/Emu/Cell/SPURecompiler.cpp | 19 ++++++++--------- rpcs3/Emu/Cell/SPUThread.cpp | 36 ++++++++++++++++++++------------ rpcs3/Emu/Cell/SPUThread.h | 4 ++-- 4 files changed, 35 insertions(+), 26 deletions(-) diff --git a/rpcs3/Emu/Cell/RawSPUThread.cpp b/rpcs3/Emu/Cell/RawSPUThread.cpp index fa27229ca9..b322ab017f 100644 --- a/rpcs3/Emu/Cell/RawSPUThread.cpp +++ b/rpcs3/Emu/Cell/RawSPUThread.cpp @@ -383,7 +383,7 @@ void spu_load_exec(const spu_exec_object& elf) spu->status_npc = {SPU_STATUS_RUNNING, elf.header.e_entry}; atomic_storage::release(spu->pc, elf.header.e_entry); - const auto funcs = spu->discover_functions(spu->ls, umax); + const auto funcs = spu->discover_functions(0, { spu->ls , SPU_LS_SIZE }, true, umax); for (u32 addr : funcs) { diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index 45e08df319..1441ed91d0 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -561,10 +561,7 @@ extern void utilize_spu_data_segment(u32 vaddr, const void* ls_data_vaddr, u32 s spu_section_data::data_t obj{vaddr, std::move(data)}; - std::vector ls_data(SPU_LS_SIZE); - std::memcpy(ls_data.data() + vaddr, ls_data_vaddr, size); - - obj.funcs = spu_thread::discover_functions(ls_data.data(), umax); + obj.funcs = spu_thread::discover_functions(vaddr, { reinterpret_cast(ls_data_vaddr), size }, true, umax); if (obj.funcs.empty()) { @@ -2111,7 +2108,7 @@ void spu_recompiler_base::old_interpreter(spu_thread& spu, void* ls, u8* /*rip*/ } } -std::vector spu_thread::discover_functions(const void* ls_start, u32 /*entry*/) +std::vector spu_thread::discover_functions(u32 base_addr, std::span ls, bool is_known_addr, u32 /*entry*/) { std::vector calls; calls.reserve(100); @@ -2119,14 +2116,16 @@ std::vector spu_thread::discover_functions(const void* ls_start, u32 /*entr // Discover functions // Use the most simple method: search for instructions that calls them // And then filter invalid cases (does not detect tail calls) - for (u32 i = 0x10; i < SPU_LS_SIZE; i += 0x10) + const v128 brasl_mask = is_known_addr ? v128::from32p(0x62) : v128::from32p(umax); + + for (u32 i = utils::align(base_addr, 0x10); i < std::min(base_addr + ls.size(), 0x3FFF0); i += 0x10) { // Search for BRSL and BRASL // TODO: BISL - const v128 inst = read_from_ptr>(static_cast(ls_start), i); + const v128 inst = read_from_ptr>(ls.data(), i - base_addr); const v128 shifted = gv_shr32(inst, 23); const v128 eq_brsl = gv_eq32(shifted, v128::from32p(0x66)); - const v128 eq_brasl = gv_eq32(shifted, v128::from32p(0x62)); + const v128 eq_brasl = gv_eq32(shifted, brasl_mask); const v128 result = eq_brsl | eq_brasl; if (!gv_testz(result)) @@ -2144,14 +2143,14 @@ std::vector spu_thread::discover_functions(const void* ls_start, u32 /*entr calls.erase(std::remove_if(calls.begin(), calls.end(), [&](u32 caller) { // Check the validity of both the callee code and the following caller code - return !is_exec_code(caller, ls_start) || !is_exec_code(caller + 4, ls_start); + return !is_exec_code(caller, ls, base_addr) || !is_exec_code(caller + 4, ls, base_addr); }), calls.end()); std::vector addrs; for (u32 addr : calls) { - const spu_opcode_t op{read_from_ptr>(static_cast(ls_start), addr)}; + const spu_opcode_t op{read_from_ptr>(ls, addr - base_addr)}; const u32 func = op_branch_targets(addr, op)[0]; diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index e5648f9cd0..45d3f87234 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -1196,7 +1196,7 @@ void spu_thread::dump_regs(std::string& ret, std::any& /*custom_data*/) const } } - if (i3 >= 0x80 && is_exec_code(i3, ls)) + if (i3 >= 0x80 && is_exec_code(i3, { ls, SPU_LS_SIZE })) { dis_asm.disasm(i3); fmt::append(ret, " -> %s", dis_asm.last_opcode); @@ -1300,7 +1300,7 @@ std::vector> spu_thread::dump_callstack_list() const return true; } - return !addr || !is_exec_code(addr, ls); + return !addr || !is_exec_code(addr, { ls, SPU_LS_SIZE }); }; if (first && lr._u32[3] != gpr0._u32[3] && !is_invalid(gpr0)) @@ -4019,17 +4019,22 @@ bool spu_thread::check_mfc_interrupts(u32 next_pc) return false; } -bool spu_thread::is_exec_code(u32 addr, const void* ls_ptr) +bool spu_thread::is_exec_code(u32 addr, std::span ls_ptr, u32 base_addr) { - if (addr & ~0x3FFFC) - { - return false; - } - for (u32 i = 0; i < 30; i++) { + if (addr & ~0x3FFFC) + { + return false; + } + + if (addr < base_addr || addr >= base_addr + ls_ptr.size()) + { + return false; + } + const u32 addr0 = spu_branch_target(addr); - const u32 op = read_from_ptr>(static_cast(ls_ptr) + addr0); + const u32 op = read_from_ptr>(ls_ptr, addr0 - base_addr); const auto type = s_spu_itype.decode(op); if (type == spu_itype::UNK || !op) @@ -4055,9 +4060,14 @@ bool spu_thread::is_exec_code(u32 addr, const void* ls_ptr) continue; } + if (route_pc < base_addr || route_pc >= base_addr + ls_ptr.size()) + { + return false; + } + // Test the validity of a single instruction of the optional target // This function can't be too slow and is unlikely to improve results by a great deal - const u32 op0 = read_from_ptr>(static_cast(ls_ptr) + route_pc); + const u32 op0 = read_from_ptr>(ls_ptr, route_pc - base_addr); const auto type0 = s_spu_itype.decode(op); if (type == spu_itype::UNK || !op) @@ -6151,12 +6161,12 @@ spu_exec_object spu_thread::capture_memory_as_elf(std::span>(all_data.data(), pc0 - 4); + const u32 op = read_from_ptr>(all_data, pc0 - 4); // Try to find function entry (if they are placed sequentially search for BI $LR of previous function) if (!op || op == 0x35000000u || s_spu_itype.decode(op) == spu_itype::UNK) { - if (is_exec_code(pc0, all_data.data())) + if (is_exec_code(pc0, { all_data.data(), SPU_LS_SIZE })) break; } } @@ -6166,7 +6176,7 @@ spu_exec_object spu_thread::capture_memory_as_elf(std::span discover_functions(const void* ls_start, u32 /*entry*/); + static bool is_exec_code(u32 addr, std::span ls_ptr, u32 base_addr = 0); // Only a hint, do not rely on it other than debugging purposes + static std::vector discover_functions(u32 base_addr, std::span ls, bool is_known_addr, u32 /*entry*/); u32 get_ch_count(u32 ch); s64 get_ch_value(u32 ch); bool set_ch_value(u32 ch, u32 value); From ee9477dc21f8052d95a5144a3efadf2a787e23fe Mon Sep 17 00:00:00 2001 From: Eladash Date: Tue, 29 Aug 2023 15:32:26 +0300 Subject: [PATCH 148/184] SPU: support pure SPU code precompilation discovery --- rpcs3/Emu/Cell/PPUModule.cpp | 186 ++++++++++++++++++++++++++++++- rpcs3/Emu/Cell/SPURecompiler.cpp | 10 +- rpcs3/Loader/ELF.h | 7 ++ 3 files changed, 198 insertions(+), 5 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index 62f80e42b1..6e100adfa4 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -13,6 +13,7 @@ #include "Emu/VFS.h" #include "Emu/Cell/PPUOpcodes.h" +#include "Emu/Cell/SPUThread.h" #include "Emu/Cell/PPUAnalyser.h" #include "Emu/Cell/lv2/sys_process.h" @@ -1070,12 +1071,186 @@ static void ppu_check_patch_spu_images(const ppu_module& mod, const ppu_segment& return; } + const bool is_firmware = mod.path.starts_with(vfs::get("/dev_flash/")); + const std::string_view seg_view{ensure(mod.get_ptr(seg.addr)), seg.size}; - for (usz i = seg_view.find("\177ELF"); i < seg.size; i = seg_view.find("\177ELF", i + 4)) + auto find_first_of_multiple = [](std::string_view data, std::initializer_list values, usz index) + { + usz pos = umax; + + for (std::string_view value : values) + { + if (usz pos0 = data.substr(index, pos - index).find(value); pos0 != umax && pos0 + index < pos) + { + pos = pos0 + index; + } + } + + return pos; + }; + + extern void utilize_spu_data_segment(u32 vaddr, const void* ls_data_vaddr, u32 size); + + // Search for [stqd lr,0x10(sp)] instruction or ELF file signature, whichever comes first + const std::initializer_list prefixes = {"\177ELF"sv, "\x24\0\x40\x80"sv}; + + usz prev_bound = 0; + + for (usz i = find_first_of_multiple(seg_view, prefixes, 0); i < seg.size; i = find_first_of_multiple(seg_view, prefixes, utils::align(i + 1, 4))) { const auto elf_header = ensure(mod.get_ptr(seg.addr + i)); + if (i % 4 == 0 && std::memcmp(elf_header, "\x24\0\x40\x80", 4) == 0) + { + bool next = true; + const u32 old_i = i; + + for (u32 search = i & -128, tries = 10; tries && search >= prev_bound; tries--, search = utils::sub_saturate(search, 128)) + { + if (seg_view[search] != 0x42 && seg_view[search] != 0x43) + { + continue; + } + + const u32 inst1 = read_from_ptr>(seg_view, search); + const u32 inst2 = read_from_ptr>(seg_view, search + 4); + const u32 inst3 = read_from_ptr>(seg_view, search + 8); + const u32 inst4 = read_from_ptr>(seg_view, search + 12); + + if ((inst1 & 0xfe'00'00'7f) != 0x42000002 || (inst2 & 0xfe'00'00'7f) != 0x42000002 || (inst3 & 0xfe'00'00'7f) != 0x42000002 || (inst4 & 0xfe'00'00'7f) != 0x42000002) + { + continue; + } + + ppu_log.success("Found SPURS GUID Pattern at 0x%05x", search + seg.addr); + i = search; + next = false; + break; + } + + if (next) + { + continue; + } + + std::string_view ls_segment = seg_view.substr(i); + + // Bound to a bit less than LS size + ls_segment = ls_segment.substr(0, 0x38000); + + for (usz addr_last = 0, valid_count = 0, invalid_count = 0;;) + { + usz instruction = ls_segment.find("\x24\0\x40\x80"sv, addr_last); + + if (instruction != umax) + { + if (instruction % 4 != i % 4) + { + // Unaligned, continue + addr_last = instruction + (i % 4 - instruction % 4) % 4; + continue; + } + + // FIXME: This seems to terminate SPU code prematurely in some cases + // Likely due to absolute branches + if (spu_thread::is_exec_code(instruction, {reinterpret_cast(ls_segment.data()), ls_segment.size()}, 0)) + { + addr_last = instruction + 4; + valid_count++; + invalid_count = 0; + continue; + } + + if (invalid_count == 0) + { + // Allow a single case of invalid data + addr_last = instruction + 4; + invalid_count++; + continue; + } + + addr_last = instruction; + } + + if (addr_last >= 0x80 && valid_count >= 2) + { + const u32 begin = i & -128; + u32 end = std::min(seg.size, utils::align(i + addr_last + 256, 128)); + + u32 guessed_ls_addr = 0; + + // Try to guess LS address by observing the pattern for disable/enable interrupts + // ILA R2, PC + 8 + // BIE/BID R2 + + for (u32 found = 0, last_vaddr = 0, it = begin + 16; it < end - 16; it += 4) + { + const u32 inst1 = read_from_ptr>(seg_view, it); + const u32 inst2 = read_from_ptr>(seg_view, it + 4); + const u32 inst3 = read_from_ptr>(seg_view, it + 8); + const u32 inst4 = read_from_ptr>(seg_view, it + 12); + + if ((inst1 & 0xfe'00'00'7f) == 0x42000002 && (inst2 & 0xfe'00'00'7f) == 0x42000002 && (inst3 & 0xfe'00'00'7f) == 0x42000002 && (inst4 & 0xfe'00'00'7f) == 0x42000002) + { + // SPURS GUID pattern + end = it; + ppu_log.success("Found SPURS GUID Pattern for terminagtor at 0x%05x", end + seg.addr); + break; + } + + if ((inst1 >> 7) % 4 == 0 && (inst1 & 0xfe'00'00'7f) == 0x42000002 && (inst2 == 0x35040100 || inst2 == 0x35080100)) + { + const u32 addr_inst = (inst1 >> 7) % 0x40000; + + if (u32 addr_seg = addr_inst - std::min(it + 8 - begin, addr_inst)) + { + if (last_vaddr != addr_seg) + { + guessed_ls_addr = 0; + found = 0; + } + + found++; + last_vaddr = addr_seg; + + if (found >= 2) + { + // Good segment address + guessed_ls_addr = last_vaddr; + ppu_log.notice("Found IENABLE/IDSIABLE Pattern at 0x%05x", it + seg.addr); + } + } + } + } + + if (guessed_ls_addr) + { + end = begin + std::min(end - begin, SPU_LS_SIZE - guessed_ls_addr); + } + + ppu_log.success("Found valid roaming SPU code at 0x%x..0x%x (guessed_ls_addr=0x%x)", seg.addr + begin, seg.addr + end, guessed_ls_addr); + + if (!is_firmware) + { + // Siginify that the base address is unknown by passing 0 + utilize_spu_data_segment(guessed_ls_addr ? guessed_ls_addr : 0x4000, seg_view.data() + begin, end - begin); + } + + i = std::max(end, i + 4) - 4; + prev_bound = i + 4; + } + else + { + i = old_i; + } + + break; + } + + continue; + } + // Try to load SPU image const spu_exec_object obj(fs::file(elf_header, seg.size - i)); @@ -1107,7 +1282,7 @@ static void ppu_check_patch_spu_images(const ppu_module& mod, const ppu_segment& if (prog.p_type == 0x1u /* LOAD */ && prog.p_filesz > 0u) { - if (prog.p_vaddr) + if (prog.p_vaddr && !is_firmware) { extern void utilize_spu_data_segment(u32 vaddr, const void* ls_data_vaddr, u32 size); @@ -1126,11 +1301,13 @@ static void ppu_check_patch_spu_images(const ppu_module& mod, const ppu_segment& if (!name.empty()) { - fmt::append(dump, "\n\tSPUNAME: '%s' (image addr: 0x%x)", name, seg.addr + i); + fmt::append(dump, "\n\tSPUNAME: '%s'", name); } } } + fmt::append(dump, " (image addr: 0x%x, size: 0x%x)", seg.addr + i, obj.highest_offset); + sha1_finish(&sha2, sha1_hash); // Format patch name @@ -1173,6 +1350,9 @@ static void ppu_check_patch_spu_images(const ppu_module& mod, const ppu_segment& { ppu_loader.success("SPU executable hash: %s (<- %u)%s", hash, applied.size(), dump); } + + i += obj.highest_offset - 4; + prev_bound = i + 4; } } diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index 1441ed91d0..55b5e3c8dc 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -561,7 +561,7 @@ extern void utilize_spu_data_segment(u32 vaddr, const void* ls_data_vaddr, u32 s spu_section_data::data_t obj{vaddr, std::move(data)}; - obj.funcs = spu_thread::discover_functions(vaddr, { reinterpret_cast(ls_data_vaddr), size }, true, umax); + obj.funcs = spu_thread::discover_functions(vaddr, { reinterpret_cast(ls_data_vaddr), size }, vaddr != 0, umax); if (obj.funcs.empty()) { @@ -703,7 +703,7 @@ void spu_cache::initialize(bool build_existing_cache) total_precompile += sec.funcs.size(); } - const bool spu_precompilation_enabled = (build_existing_cache ? func_list.empty() : func_list.size() < total_precompile) && g_cfg.core.spu_cache && g_cfg.core.llvm_precompilation; + const bool spu_precompilation_enabled = func_list.empty() && g_cfg.core.spu_cache && g_cfg.core.llvm_precompilation; if (spu_precompilation_enabled) { @@ -716,6 +716,7 @@ void spu_cache::initialize(bool build_existing_cache) } else { + total_precompile = 0; data_list.clear(); } @@ -959,12 +960,17 @@ void spu_cache::initialize(bool build_existing_cache) return result; }); + u32 built_total = 0; + // Join (implicitly) and print individual results for (u32 i = 0; i < workers.size(); i++) { spu_log.notice("SPU Runtime: Worker %u built %u programs.", i + 1, workers[i]); + built_total += workers[i]; } + spu_log.notice("SPU Runtime: Workers built %u programs.", built_total); + if (Emu.IsStopped()) { spu_log.error("SPU Runtime: Cache building aborted."); diff --git a/rpcs3/Loader/ELF.h b/rpcs3/Loader/ELF.h index abb9979c19..34bb06c267 100644 --- a/rpcs3/Loader/ELF.h +++ b/rpcs3/Loader/ELF.h @@ -260,6 +260,8 @@ public: std::vector progs{}; std::vector shdrs{}; + usz highest_offset = 0; + public: elf_object() = default; @@ -270,6 +272,8 @@ public: elf_error open(const fs::file& stream, u64 offset = 0, bs_t opts = {}) { + highest_offset = 0; + // Check stream if (!stream) return set_error(elf_error::stream); @@ -322,6 +326,7 @@ public: stream.seek(offset + header.e_phoff); if (!stream.read(_phdrs, header.e_phnum)) return set_error(elf_error::stream_phdrs); + highest_offset = std::max(highest_offset, stream.pos()); } if (!(opts & elf_opt::no_sections)) @@ -329,6 +334,7 @@ public: stream.seek(offset + header.e_shoff); if (!stream.read(_shdrs, header.e_shnum)) return set_error(elf_error::stream_shdrs); + highest_offset = std::max(highest_offset, stream.pos()); } progs.clear(); @@ -342,6 +348,7 @@ public: stream.seek(offset + hdr.p_offset); if (!stream.read(progs.back().bin, hdr.p_filesz)) return set_error(elf_error::stream_data); + highest_offset = std::max(highest_offset, stream.pos()); } } From 6d0390bad97c67909db372d70c6c0086b8c70874 Mon Sep 17 00:00:00 2001 From: Eladash Date: Tue, 29 Aug 2023 21:44:57 +0300 Subject: [PATCH 149/184] Fixup Create PPU Cache --- rpcs3/Emu/System.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index cba2644511..7b0aa144d6 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -1467,7 +1467,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, if (obj == elf_error::ok && ppu_load_exec(obj, true, path)) { - g_fxo->get().path = path; + ensure(g_fxo->try_get())->path = path; } else { @@ -1478,13 +1478,14 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, g_fxo->init("SPRX Loader"sv, [this, dir_queue]() mutable { - if (auto& _main = g_fxo->get(); !_main.path.empty()) + if (auto& _main = *ensure(g_fxo->try_get()); !_main.path.empty()) { if (!_main.analyse(0, _main.elf_entry, _main.seg0_code_end, _main.applied_pathes, [](){ return Emu.IsStopped(); })) { return; } + Emu.ConfigurePPUCache(); ppu_initialize(_main); } From ba41e466cf34cf19dbdb0fcebd5b192e834b7c47 Mon Sep 17 00:00:00 2001 From: Eladash Date: Wed, 30 Aug 2023 15:43:01 +0300 Subject: [PATCH 150/184] Hotfix SPU Cache Spam For Game Collections --- rpcs3/Emu/Cell/PPUModule.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index 6e100adfa4..c3bd69ea4b 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -1073,6 +1073,8 @@ static void ppu_check_patch_spu_images(const ppu_module& mod, const ppu_segment& const bool is_firmware = mod.path.starts_with(vfs::get("/dev_flash/")); + const auto _main = g_fxo->try_get(); + const std::string_view seg_view{ensure(mod.get_ptr(seg.addr)), seg.size}; auto find_first_of_multiple = [](std::string_view data, std::initializer_list values, usz index) @@ -1231,7 +1233,7 @@ static void ppu_check_patch_spu_images(const ppu_module& mod, const ppu_segment& ppu_log.success("Found valid roaming SPU code at 0x%x..0x%x (guessed_ls_addr=0x%x)", seg.addr + begin, seg.addr + end, guessed_ls_addr); - if (!is_firmware) + if (!is_firmware && _main == &mod) { // Siginify that the base address is unknown by passing 0 utilize_spu_data_segment(guessed_ls_addr ? guessed_ls_addr : 0x4000, seg_view.data() + begin, end - begin); @@ -1282,7 +1284,7 @@ static void ppu_check_patch_spu_images(const ppu_module& mod, const ppu_segment& if (prog.p_type == 0x1u /* LOAD */ && prog.p_filesz > 0u) { - if (prog.p_vaddr && !is_firmware) + if (prog.p_vaddr && !is_firmware && _main == &mod) { extern void utilize_spu_data_segment(u32 vaddr, const void* ls_data_vaddr, u32 size); From 02b5cae2ad377499cb57cf9ab0cd64f94a9af57e Mon Sep 17 00:00:00 2001 From: Eladash Date: Wed, 30 Aug 2023 16:08:27 +0300 Subject: [PATCH 151/184] Precompile PRX/ELF extension --- rpcs3/Emu/Cell/PPUThread.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 2602afdfcd..8b53f4ca33 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -3667,8 +3667,8 @@ extern void ppu_precompile(std::vector& dir_queue, std::vector& dir_queue, std::vector Date: Wed, 30 Aug 2023 09:56:18 -0700 Subject: [PATCH 152/184] Update linux Azure and re-enable GitHub pushing --- azure-pipelines.yml | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 20af381970..4c73638322 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -35,13 +35,13 @@ jobs: displayName: ccache - bash: | - docker pull --quiet rpcs3/rpcs3-ci-bionic:1.3 + docker pull --quiet rpcs3/rpcs3-ci-focal:1.1 docker run \ -v $(pwd):/rpcs3 \ --env-file .ci/docker.env \ -v $CCACHE_DIR:/root/.ccache \ -v $BUILD_ARTIFACTSTAGINGDIRECTORY:/root/artifacts \ - rpcs3/rpcs3-ci-bionic:1.3 \ + rpcs3/rpcs3-ci-focal:1.1 \ /rpcs3/.ci/build-linux.sh displayName: Docker setup and build @@ -49,18 +49,18 @@ jobs: condition: succeeded() artifact: RPCS3 for Linux ($(COMPILER)) - # - bash: | - # COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ./rpcs3/rpcs3_version.cpp) - # COMM_COUNT=$(git rev-list --count HEAD) - # COMM_HASH=$(git rev-parse --short=8 HEAD) + - bash: | + COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ./rpcs3/rpcs3_version.cpp) + COMM_COUNT=$(git rev-list --count HEAD) + COMM_HASH=$(git rev-parse --short=8 HEAD) - # export AVVER="${COMM_TAG}-${COMM_COUNT}" + export AVVER="${COMM_TAG}-${COMM_COUNT}" - # .ci/github-upload.sh - # condition: and(ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.Repository.Name'], 'RPCS3/rpcs3'), eq(variables['Build.SourceBranch'], 'refs/heads/master'), eq(variables['COMPILER'], 'gcc')) - # displayName: Push build to GitHub - # env: - # RPCS3_TOKEN: $(RPCS3-Token) + .ci/github-upload.sh + condition: and(ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.Repository.Name'], 'RPCS3/rpcs3'), eq(variables['Build.SourceBranch'], 'refs/heads/master'), eq(variables['COMPILER'], 'gcc')) + displayName: Push build to GitHub + env: + RPCS3_TOKEN: $(RPCS3-Token) - job: Windows_Build variables: @@ -121,8 +121,9 @@ jobs: condition: succeeded() artifact: RPCS3 for Windows - # - bash: .ci/github-upload.sh - # condition: and(ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.Repository.Name'], 'RPCS3/rpcs3'), eq(variables['Build.SourceBranch'], 'refs/heads/master')) - # displayName: Push build to GitHub - # env: - # RPCS3_TOKEN: $(RPCS3-Token) + - bash: | + .ci/github-upload.sh + condition: and(ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.Repository.Name'], 'RPCS3/rpcs3'), eq(variables['Build.SourceBranch'], 'refs/heads/master')) + displayName: Push build to GitHub + env: + RPCS3_TOKEN: $(RPCS3-Token) From c0a7db88996ac84027437059d04aab207ab0e2ed Mon Sep 17 00:00:00 2001 From: Zion Nimchuk Date: Wed, 30 Aug 2023 10:09:23 -0700 Subject: [PATCH 153/184] More cleanup --- .ci/build-linux.sh | 2 + .cirrus.yml | 177 +++++++++++++++++++++----------------------- azure-pipelines.yml | 11 +-- 3 files changed, 89 insertions(+), 101 deletions(-) diff --git a/.ci/build-linux.sh b/.ci/build-linux.sh index 3e42e6d2bb..af43e6b0ab 100755 --- a/.ci/build-linux.sh +++ b/.ci/build-linux.sh @@ -4,6 +4,8 @@ if [ -z "$CIRRUS_CI" ]; then cd rpcs3 || exit 1 fi +git config --global --add safe.directory '*' + # Pull all the submodules except llvm # Note: Tried to use git submodule status, but it takes over 20 seconds # shellcheck disable=SC2046 diff --git a/.cirrus.yml b/.cirrus.yml index 144feb8efd..9662ce54e9 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -9,100 +9,95 @@ env: QT_VER_MAIN: '6' QT_VER: '6.5.2' -windows_task: - matrix: - - name: Cirrus Windows - windows_container: - image: cirrusci/windowsservercore:visualstudio2019 - cpu: 8 - memory: 16G - env: - CIRRUS_SHELL: "bash" - COMPILER: msvc - BUILD_ARTIFACTSTAGINGDIRECTORY: ${CIRRUS_WORKING_DIR}\artifacts\ - QT_VER_MSVC: 'msvc2019' - QT_DATE: '202307080351' - QTDIR: C:\Qt\${QT_VER}\${QT_VER_MSVC}_64 - VULKAN_VER: '1.3.224.1' - VULKAN_SDK_SHA: '2029e652e39ee6a6036cff3765da31e1e6c595fd2413d3cd111dfab7855621ea' - VULKAN_SDK: C:\VulkanSDK\${VULKAN_VER} - CACHE_DIR: "./cache" - UPLOAD_COMMIT_HASH: 7d09e3be30805911226241afbb14f8cdc2eb054e - UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-win" - deps_cache: - folder: "./cache" - #obj_cache: - # folder: "./tmp" - #obj2_cache: - # folder: "./rpcs3/x64" - setup_script: - - './.ci/get_keys-windows.sh' - - './.ci/setup-windows.sh' -# - choco install -y python # Needed for SPIRV, use either this or make a new Docker image -# spirv_script: -# - export PATH=${PATH}:"C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin" -# - cd "${CIRRUS_WORKING_DIR}/3rdparty/SPIRV" -# - msbuild.exe spirv.vcxproj //p:Configuration=Release //m - rpcs3_script: - - export PATH=${PATH}:"C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin" - - msbuild.exe rpcs3.sln //p:Configuration=Release //m - deploy_script: - - mkdir artifacts - - source './.ci/export-cirrus-vars.sh' - - './.ci/deploy-windows.sh' - artifacts: - name: Artifact - path: "*.7z*" - push_script: | - if [ "$CIRRUS_REPO_OWNER" = "RPCS3" ] && [ -z "$CIRRUS_PR" ] && [ "$CIRRUS_BRANCH" = "master" ]; then - source './.ci/export-cirrus-vars.sh' - './.ci/github-upload.sh' - fi; +# windows_task: +# matrix: +# - name: Cirrus Windows +# windows_container: +# image: cirrusci/windowsservercore:visualstudio2019 +# cpu: 8 +# memory: 16G +# env: +# CIRRUS_SHELL: "bash" +# COMPILER: msvc +# BUILD_ARTIFACTSTAGINGDIRECTORY: ${CIRRUS_WORKING_DIR}\artifacts\ +# QT_VER_MSVC: 'msvc2019' +# QT_DATE: '202307080351' +# QTDIR: C:\Qt\${QT_VER}\${QT_VER_MSVC}_64 +# VULKAN_VER: '1.3.224.1' +# VULKAN_SDK_SHA: '2029e652e39ee6a6036cff3765da31e1e6c595fd2413d3cd111dfab7855621ea' +# VULKAN_SDK: C:\VulkanSDK\${VULKAN_VER} +# CACHE_DIR: "./cache" +# UPLOAD_COMMIT_HASH: 7d09e3be30805911226241afbb14f8cdc2eb054e +# UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-win" +# deps_cache: +# folder: "./cache" +# #obj_cache: +# # folder: "./tmp" +# #obj2_cache: +# # folder: "./rpcs3/x64" +# setup_script: +# - './.ci/get_keys-windows.sh' +# - './.ci/setup-windows.sh' +# rpcs3_script: +# - export PATH=${PATH}:"C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin" +# - msbuild.exe rpcs3.sln //p:Configuration=Release //m +# deploy_script: +# - mkdir artifacts +# - source './.ci/export-cirrus-vars.sh' +# - './.ci/deploy-windows.sh' +# artifacts: +# name: Artifact +# path: "*.7z*" +# push_script: | +# if [ "$CIRRUS_REPO_OWNER" = "RPCS3" ] && [ -z "$CIRRUS_PR" ] && [ "$CIRRUS_BRANCH" = "master" ]; then +# source './.ci/export-cirrus-vars.sh' +# './.ci/github-upload.sh' +# fi; -linux_task: - container: - image: rpcs3/rpcs3-ci-focal:1.1 - cpu: 4 - memory: 16G - env: - BUILD_ARTIFACTSTAGINGDIRECTORY: ${CIRRUS_WORKING_DIR}/artifacts - ARTDIR: ${CIRRUS_WORKING_DIR}/artifacts/ - CCACHE_DIR: "/tmp/ccache_dir" - CCACHE_MAXSIZE: 300M - CI_HAS_ARTIFACTS: true - UPLOAD_COMMIT_HASH: d812f1254a1157c80fd402f94446310560f54e5f - UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux" - DEPLOY_APPIMAGE: true - APPDIR: "./appdir" - RELEASE_MESSAGE: "../GitHubReleaseMessage.txt" - ccache_cache: - folder: "/tmp/ccache_dir" - matrix: - - name: Cirrus Linux GCC - env: - COMPILER: gcc - gcc_script: - - mkdir artifacts - - ".ci/build-linux.sh" - - name: Cirrus Linux Clang - env: - COMPILER: clang - clang_script: - - mkdir artifacts - - ".ci/build-linux.sh" - artifacts: - name: Artifact - path: "artifacts/*" - push_script: | - if [ "$CIRRUS_REPO_OWNER" = "RPCS3" ] && [ -z "$CIRRUS_PR" ] && [ "$CIRRUS_BRANCH" = "master" ] && [ "$COMPILER" = "gcc" ]; then - COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ./rpcs3/rpcs3_version.cpp) - COMM_COUNT=$(git rev-list --count HEAD) - COMM_HASH=$(git rev-parse --short=8 HEAD) +# linux_task: +# container: +# image: rpcs3/rpcs3-ci-focal:1.1 +# cpu: 4 +# memory: 16G +# env: +# BUILD_ARTIFACTSTAGINGDIRECTORY: ${CIRRUS_WORKING_DIR}/artifacts +# ARTDIR: ${CIRRUS_WORKING_DIR}/artifacts/ +# CCACHE_DIR: "/tmp/ccache_dir" +# CCACHE_MAXSIZE: 300M +# CI_HAS_ARTIFACTS: true +# UPLOAD_COMMIT_HASH: d812f1254a1157c80fd402f94446310560f54e5f +# UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux" +# DEPLOY_APPIMAGE: true +# APPDIR: "./appdir" +# RELEASE_MESSAGE: "../GitHubReleaseMessage.txt" +# ccache_cache: +# folder: "/tmp/ccache_dir" +# matrix: +# - name: Cirrus Linux GCC +# env: +# COMPILER: gcc +# gcc_script: +# - mkdir artifacts +# - ".ci/build-linux.sh" +# - name: Cirrus Linux Clang +# env: +# COMPILER: clang +# clang_script: +# - mkdir artifacts +# - ".ci/build-linux.sh" +# artifacts: +# name: Artifact +# path: "artifacts/*" +# push_script: | +# if [ "$CIRRUS_REPO_OWNER" = "RPCS3" ] && [ -z "$CIRRUS_PR" ] && [ "$CIRRUS_BRANCH" = "master" ] && [ "$COMPILER" = "gcc" ]; then +# COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ./rpcs3/rpcs3_version.cpp) +# COMM_COUNT=$(git rev-list --count HEAD) +# COMM_HASH=$(git rev-parse --short=8 HEAD) - export AVVER="${COMM_TAG}-${COMM_COUNT}" +# export AVVER="${COMM_TAG}-${COMM_COUNT}" - .ci/github-upload.sh - fi; +# .ci/github-upload.sh +# fi; freebsd_task: matrix: diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4c73638322..d99f9d83c0 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -98,14 +98,6 @@ jobs: - bash: .ci/export-azure-vars.sh displayName: Export Variables - - task: MSBuild@1 - inputs: - solution: './3rdparty/SPIRV/spirv.vcxproj' - maximumCpuCount: true - platform: x64 - configuration: 'Release' - displayName: Compile SPIRV-Tools - - task: VSBuild@1 inputs: solution: 'rpcs3.sln' @@ -121,8 +113,7 @@ jobs: condition: succeeded() artifact: RPCS3 for Windows - - bash: | - .ci/github-upload.sh + - bash: .ci/github-upload.sh condition: and(ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.Repository.Name'], 'RPCS3/rpcs3'), eq(variables['Build.SourceBranch'], 'refs/heads/master')) displayName: Push build to GitHub env: From cb8aa21fb1fa0c9bfb44f0017749bbe154a61ca7 Mon Sep 17 00:00:00 2001 From: Zion Nimchuk Date: Wed, 30 Aug 2023 12:56:54 -0700 Subject: [PATCH 154/184] Make sure to enable PR builds --- azure-pipelines.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d99f9d83c0..f19a4e927f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -5,7 +5,10 @@ trigger: tags: exclude: - '*' -pr: none +pr: + branches: + include: + - master jobs: - job: Linux_Build strategy: From f554b444c0b596d45d854807659f941af65f3c76 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 29 Aug 2023 21:05:04 +0200 Subject: [PATCH 155/184] Qt: rename pad profiles to config files --- rpcs3/Emu/Io/pad_config.cpp | 44 +++++++------- rpcs3/Emu/Io/pad_config.h | 10 +-- rpcs3/Emu/system_utils.cpp | 2 +- rpcs3/Input/pad_thread.cpp | 16 ++--- rpcs3/main.cpp | 18 +++--- rpcs3/rpcs3qt/game_list_frame.cpp | 8 +-- rpcs3/rpcs3qt/pad_settings_dialog.cpp | 88 +++++++++++++-------------- rpcs3/rpcs3qt/pad_settings_dialog.h | 8 +-- rpcs3/rpcs3qt/pad_settings_dialog.ui | 12 ++-- 9 files changed, 103 insertions(+), 103 deletions(-) diff --git a/rpcs3/Emu/Io/pad_config.cpp b/rpcs3/Emu/Io/pad_config.cpp index 3e42ed684b..4fd5ec080d 100644 --- a/rpcs3/Emu/Io/pad_config.cpp +++ b/rpcs3/Emu/Io/pad_config.cpp @@ -4,7 +4,7 @@ LOG_CHANNEL(input_log, "Input"); -extern std::string g_pad_profile_override; +extern std::string g_input_config_override; std::vector cfg_pad::get_buttons(const std::string& str) { @@ -26,16 +26,16 @@ std::string cfg_pad::get_buttons(std::vector vec) return fmt::merge(vec, ","); } -bool cfg_input::load(const std::string& title_id, const std::string& profile, bool strict) +bool cfg_input::load(const std::string& title_id, const std::string& config_file, bool strict) { - input_log.notice("Loading pad config (title_id='%s', profile='%s', strict=%d)", title_id, profile, strict); + input_log.notice("Loading pad config (title_id='%s', config_file='%s', strict=%d)", title_id, config_file, strict); std::string cfg_name; - // Check profile override first - if (!strict && !g_pad_profile_override.empty()) + // Check configuration override first + if (!strict && !g_input_config_override.empty()) { - cfg_name = rpcs3::utils::get_input_config_dir() + g_pad_profile_override + ".yml"; + cfg_name = rpcs3::utils::get_input_config_dir() + g_input_config_override + ".yml"; } // Check custom config next @@ -44,23 +44,23 @@ bool cfg_input::load(const std::string& title_id, const std::string& profile, bo cfg_name = rpcs3::utils::get_custom_input_config_path(title_id); } - // Check active global profile next - if ((title_id.empty() || !strict) && !profile.empty() && !fs::is_file(cfg_name)) + // Check active global configuration next + if ((title_id.empty() || !strict) && !config_file.empty() && !fs::is_file(cfg_name)) { - cfg_name = rpcs3::utils::get_input_config_dir() + profile + ".yml"; + cfg_name = rpcs3::utils::get_input_config_dir() + config_file + ".yml"; } - // Fallback to default profile + // Fallback to default configuration if (!strict && !fs::is_file(cfg_name)) { - cfg_name = rpcs3::utils::get_input_config_dir() + g_cfg_profile.default_profile + ".yml"; + cfg_name = rpcs3::utils::get_input_config_dir() + g_cfg_input_configs.default_config + ".yml"; } from_default(); if (fs::file cfg_file{ cfg_name, fs::read }) { - input_log.notice("Loading pad profile: '%s'", cfg_name); + input_log.notice("Loading input configuration: '%s'", cfg_name); if (std::string content = cfg_file.to_string(); !content.empty()) { @@ -69,7 +69,7 @@ bool cfg_input::load(const std::string& title_id, const std::string& profile, bo } // Add keyboard by default - input_log.notice("Pad profile empty. Adding default keyboard pad handler"); + input_log.notice("Input configuration empty. Adding default keyboard pad handler"); player[0]->handler.from_string(fmt::format("%s", pad_handler::keyboard)); player[0]->device.from_string(pad::keyboard_device_name.data()); player[0]->buddy_device.from_string(""sv); @@ -77,14 +77,14 @@ bool cfg_input::load(const std::string& title_id, const std::string& profile, bo return false; } -void cfg_input::save(const std::string& title_id, const std::string& profile) const +void cfg_input::save(const std::string& title_id, const std::string& config_file) const { std::string cfg_name; if (title_id.empty()) { - cfg_name = rpcs3::utils::get_input_config_dir() + profile + ".yml"; - input_log.notice("Saving pad config profile '%s' to '%s'", profile, cfg_name); + cfg_name = rpcs3::utils::get_input_config_dir() + config_file + ".yml"; + input_log.notice("Saving input configuration '%s' to '%s'", config_file, cfg_name); } else { @@ -105,12 +105,12 @@ void cfg_input::save(const std::string& title_id, const std::string& profile) co } } -cfg_profile::cfg_profile() - : path(rpcs3::utils::get_input_config_root() + "/active_profiles.yml") +cfg_input_configurations::cfg_input_configurations() + : path(rpcs3::utils::get_input_config_root() + "/active_input_configurations.yml") { } -bool cfg_profile::load() +bool cfg_input_configurations::load() { if (fs::file cfg_file{ path, fs::read }) { @@ -121,14 +121,14 @@ bool cfg_profile::load() return false; } -void cfg_profile::save() const +void cfg_input_configurations::save() const { - input_log.notice("Saving pad profile config to '%s'", path); + input_log.notice("Saving input configurations config to '%s'", path); fs::pending_file cfg_file(path); if (!cfg_file.file || (cfg_file.file.write(to_string()), !cfg_file.commit())) { - input_log.error("Failed to save pad profile config to '%s' (error=%s)", path, fs::g_tls_error); + input_log.error("Failed to save input configurations config to '%s' (error=%s)", path, fs::g_tls_error); } } diff --git a/rpcs3/Emu/Io/pad_config.h b/rpcs3/Emu/Io/pad_config.h index bdde9fb219..b2d34bf7c2 100644 --- a/rpcs3/Emu/Io/pad_config.h +++ b/rpcs3/Emu/Io/pad_config.h @@ -131,18 +131,18 @@ struct cfg_input final : cfg::node void save(const std::string& title_id, const std::string& profile = "") const; }; -struct cfg_profile final : cfg::node +struct cfg_input_configurations final : cfg::node { - cfg_profile(); + cfg_input_configurations(); bool load(); void save() const; const std::string path; const std::string global_key = "global"; - const std::string default_profile = "Default"; + const std::string default_config = "Default"; - cfg::map_entry active_profiles{ this, "Active Profiles" }; + cfg::map_entry active_configs{ this, "Active Configurations" }; }; extern cfg_input g_cfg_input; -extern cfg_profile g_cfg_profile; +extern cfg_input_configurations g_cfg_input_configs; diff --git a/rpcs3/Emu/system_utils.cpp b/rpcs3/Emu/system_utils.cpp index 3538c1a2be..75f4c199da 100644 --- a/rpcs3/Emu/system_utils.cpp +++ b/rpcs3/Emu/system_utils.cpp @@ -379,6 +379,6 @@ namespace rpcs3::utils std::string get_custom_input_config_path(const std::string& title_id) { if (title_id.empty()) return ""; - return get_input_config_dir(title_id) + g_cfg_profile.default_profile + ".yml"; + return get_input_config_dir(title_id) + g_cfg_input_configs.default_config + ".yml"; } } diff --git a/rpcs3/Input/pad_thread.cpp b/rpcs3/Input/pad_thread.cpp index 1afc9c54db..77187883b4 100644 --- a/rpcs3/Input/pad_thread.cpp +++ b/rpcs3/Input/pad_thread.cpp @@ -27,7 +27,7 @@ LOG_CHANNEL(sys_log, "SYS"); extern bool is_input_allowed(); -extern std::string g_pad_profile_override; +extern std::string g_input_config_override; namespace pad { @@ -100,19 +100,19 @@ void pad_thread::Init() handlers.clear(); - g_cfg_profile.load(); + g_cfg_input_configs.load(); - std::string active_profile = g_cfg_profile.active_profiles.get_value(pad::g_title_id); + std::string active_config = g_cfg_input_configs.active_configs.get_value(pad::g_title_id); - if (active_profile.empty()) + if (active_config.empty()) { - active_profile = g_cfg_profile.active_profiles.get_value(g_cfg_profile.global_key); + active_config = g_cfg_input_configs.active_configs.get_value(g_cfg_input_configs.global_key); } - input_log.notice("Using pad profile: '%s' (override='%s')", active_profile, g_pad_profile_override); + input_log.notice("Using input configuration: '%s' (override='%s')", active_config, g_input_config_override); // Load in order to get the pad handlers - if (!g_cfg_input.load(pad::g_title_id, active_profile)) + if (!g_cfg_input.load(pad::g_title_id, active_config)) { input_log.notice("Loaded empty pad config"); } @@ -125,7 +125,7 @@ void pad_thread::Init() } // Reload with proper defaults - if (!g_cfg_input.load(pad::g_title_id, active_profile)) + if (!g_cfg_input.load(pad::g_title_id, active_config)) { input_log.notice("Reloaded empty pad config"); } diff --git a/rpcs3/main.cpp b/rpcs3/main.cpp index 3dda91b80a..e5c4928b84 100644 --- a/rpcs3/main.cpp +++ b/rpcs3/main.cpp @@ -88,7 +88,7 @@ static atomic_t s_headless = false; static atomic_t s_no_gui = false; static atomic_t s_argv0; -std::string g_pad_profile_override; +std::string g_input_config_override; extern thread_local std::string(*g_tls_log_prefix)(); extern thread_local std::string_view g_tls_serialize_name; @@ -291,7 +291,7 @@ constexpr auto arg_styles = "styles"; constexpr auto arg_style = "style"; constexpr auto arg_stylesheet = "stylesheet"; constexpr auto arg_config = "config"; -constexpr auto arg_pad_profile = "pad-profile"; // only useful with no-gui +constexpr auto arg_input_config = "input-config"; // only useful with no-gui constexpr auto arg_q_debug = "qDebug"; constexpr auto arg_error = "error"; constexpr auto arg_updating = "updating"; @@ -652,8 +652,8 @@ int main(int argc, char** argv) parser.addOption(QCommandLineOption(arg_stylesheet, "Loads a custom stylesheet.", "path", "")); const QCommandLineOption config_option(arg_config, "Forces the emulator to use this configuration file for CLI-booted game.", "path", ""); parser.addOption(config_option); - const QCommandLineOption pad_profile_option(arg_pad_profile, "Forces the emulator to use this pad profile file for CLI-booted game.", "name", ""); - parser.addOption(pad_profile_option); + const QCommandLineOption input_config_option(arg_input_config, "Forces the emulator to use this input config file for CLI-booted game.", "name", ""); + parser.addOption(input_config_option); const QCommandLineOption installfw_option(arg_installfw, "Forces the emulator to install this firmware file.", "path", ""); parser.addOption(installfw_option); const QCommandLineOption installpkg_option(arg_installpkg, "Forces the emulator to install this pkg file.", "path", ""); @@ -1289,18 +1289,18 @@ int main(int argc, char** argv) } } - if (parser.isSet(arg_pad_profile)) + if (parser.isSet(arg_input_config)) { if (!s_no_gui) { - report_fatal_error(fmt::format("The option '%s' can only be used in combination with '%s'.", arg_pad_profile, arg_no_gui)); + report_fatal_error(fmt::format("The option '%s' can only be used in combination with '%s'.", arg_input_config, arg_no_gui)); } - g_pad_profile_override = parser.value(pad_profile_option).toStdString(); + g_input_config_override = parser.value(input_config_option).toStdString(); - if (g_pad_profile_override.empty()) + if (g_input_config_override.empty()) { - report_fatal_error(fmt::format("Pad profile name is empty")); + report_fatal_error(fmt::format("Input config file name is empty")); } } diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index 28181afba4..1e902fe03f 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -1684,10 +1684,10 @@ bool game_list_frame::RemoveCustomPadConfiguration(const std::string& title_id, : tr("Remove custom pad configuration?")) != QMessageBox::Yes) return true; - g_cfg_profile.load(); - g_cfg_profile.active_profiles.erase(title_id); - g_cfg_profile.save(); - game_list_log.notice("Removed active pad profile entry for key '%s'", title_id); + g_cfg_input_configs.load(); + g_cfg_input_configs.active_configs.erase(title_id); + g_cfg_input_configs.save(); + game_list_log.notice("Removed active input configuration entry for key '%s'", title_id); if (QDir(qstr(config_dir)).removeRecursively()) { diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.cpp b/rpcs3/rpcs3qt/pad_settings_dialog.cpp index b88075576e..3311ea188b 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/pad_settings_dialog.cpp @@ -30,7 +30,7 @@ LOG_CHANNEL(cfg_log, "CFG"); inline std::string sstr(const QString& _in) { return _in.toStdString(); } constexpr auto qstr = QString::fromStdString; -cfg_profile g_cfg_profile; +cfg_input_configurations g_cfg_input_configs; inline bool CreateConfigFile(const QString& dir, const QString& name) { @@ -85,38 +85,38 @@ pad_settings_dialog::pad_settings_dialog(std::shared_ptr gui_setti setWindowTitle(tr("Gamepad Settings")); } - // Load profiles - g_cfg_profile.load(); + // Load input configs + g_cfg_input_configs.load(); if (m_title_id.empty()) { - const QString profile_dir = qstr(rpcs3::utils::get_input_config_dir(m_title_id)); - QStringList profiles = gui::utils::get_dir_entries(QDir(profile_dir), QStringList() << "*.yml"); - QString active_profile = qstr(g_cfg_profile.active_profiles.get_value(g_cfg_profile.global_key)); + const QString input_config_dir = qstr(rpcs3::utils::get_input_config_dir(m_title_id)); + QStringList config_files = gui::utils::get_dir_entries(QDir(input_config_dir), QStringList() << "*.yml"); + QString active_config_file = qstr(g_cfg_input_configs.active_configs.get_value(g_cfg_input_configs.global_key)); - if (!profiles.contains(active_profile)) + if (!config_files.contains(active_config_file)) { - const QString default_profile = qstr(g_cfg_profile.default_profile); + const QString default_config_file = qstr(g_cfg_input_configs.default_config); - if (!profiles.contains(default_profile) && CreateConfigFile(profile_dir, default_profile)) + if (!config_files.contains(default_config_file) && CreateConfigFile(input_config_dir, default_config_file)) { - profiles.prepend(default_profile); + config_files.prepend(default_config_file); } - active_profile = default_profile; + active_config_file = default_config_file; } - for (const QString& profile : profiles) + for (const QString& profile : config_files) { - ui->chooseProfile->addItem(profile); + ui->chooseConfig->addItem(profile); } - ui->chooseProfile->setCurrentText(active_profile); + ui->chooseConfig->setCurrentText(active_config_file); } else { - ui->chooseProfile->addItem(qstr(m_title_id)); - ui->gb_profiles->setEnabled(false); + ui->chooseConfig->addItem(qstr(m_title_id)); + ui->gb_config_files->setEnabled(false); } // Create tab widget for 7 players @@ -143,11 +143,11 @@ pad_settings_dialog::pad_settings_dialog(std::shared_ptr gui_setti // Combobox: Devices connect(ui->chooseDevice, QOverload::of(&QComboBox::currentIndexChanged), this, &pad_settings_dialog::ChangeDevice); - // Combobox: Profiles - connect(ui->chooseProfile, &QComboBox::currentTextChanged, this, &pad_settings_dialog::ChangeProfile); + // Combobox: Configs + connect(ui->chooseConfig, &QComboBox::currentTextChanged, this, &pad_settings_dialog::ChangeConfig); - // Pushbutton: Add Profile - connect(ui->b_addProfile, &QAbstractButton::clicked, this, &pad_settings_dialog::AddProfile); + // Pushbutton: Add config file + connect(ui->b_addConfig, &QAbstractButton::clicked, this, &pad_settings_dialog::AddConfigFile); ui->buttonBox->button(QDialogButtonBox::Reset)->setText(tr("Filter Noise")); @@ -221,7 +221,7 @@ pad_settings_dialog::pad_settings_dialog(std::shared_ptr gui_setti // Set up first tab OnTabChanged(0); - ChangeProfile(ui->chooseProfile->currentText()); + ChangeConfig(ui->chooseConfig->currentText()); } void pad_settings_dialog::closeEvent(QCloseEvent* event) @@ -308,7 +308,7 @@ void pad_settings_dialog::InitButtons() insert_button(button_ids::id_pressure_intensity, ui->b_pressure_intensity); m_pad_buttons->addButton(ui->b_refresh, button_ids::id_refresh); - m_pad_buttons->addButton(ui->b_addProfile, button_ids::id_add_profile); + m_pad_buttons->addButton(ui->b_addConfig, button_ids::id_add_config_file); connect(m_pad_buttons, &QButtonGroup::idClicked, this, &pad_settings_dialog::OnPadButtonClicked); @@ -751,7 +751,7 @@ void pad_settings_dialog::ReactivateButtons() ui->tabWidget->setFocusPolicy(Qt::TabFocus); ui->scrollArea->setFocusPolicy(Qt::StrongFocus); - ui->chooseProfile->setFocusPolicy(Qt::WheelFocus); + ui->chooseConfig->setFocusPolicy(Qt::WheelFocus); ui->chooseHandler->setFocusPolicy(Qt::WheelFocus); ui->chooseDevice->setFocusPolicy(Qt::WheelFocus); ui->chooseClass->setFocusPolicy(Qt::WheelFocus); @@ -1251,7 +1251,7 @@ void pad_settings_dialog::OnPadButtonClicked(int id) case button_ids::id_led: case button_ids::id_pad_begin: case button_ids::id_pad_end: - case button_ids::id_add_profile: + case button_ids::id_add_config_file: case button_ids::id_refresh: case button_ids::id_ok: case button_ids::id_cancel: @@ -1290,7 +1290,7 @@ void pad_settings_dialog::OnPadButtonClicked(int id) ui->tabWidget->setFocusPolicy(Qt::ClickFocus); ui->scrollArea->setFocusPolicy(Qt::ClickFocus); - ui->chooseProfile->setFocusPolicy(Qt::ClickFocus); + ui->chooseConfig->setFocusPolicy(Qt::ClickFocus); ui->chooseHandler->setFocusPolicy(Qt::ClickFocus); ui->chooseDevice->setFocusPolicy(Qt::ClickFocus); ui->chooseClass->setFocusPolicy(Qt::ClickFocus); @@ -1551,15 +1551,15 @@ void pad_settings_dialog::ChangeHandler() } } -void pad_settings_dialog::ChangeProfile(const QString& profile) +void pad_settings_dialog::ChangeConfig(const QString& config_file) { - if (profile.isEmpty()) + if (config_file.isEmpty()) return; - m_profile = sstr(profile); + m_config_file = sstr(config_file); // Load in order to get the pad handlers - if (!g_cfg_input.load(m_title_id, m_profile, true)) + if (!g_cfg_input.load(m_title_id, m_config_file, true)) { cfg_log.notice("Loaded empty pad config"); } @@ -1572,7 +1572,7 @@ void pad_settings_dialog::ChangeProfile(const QString& profile) } // Reload with proper defaults - if (!g_cfg_input.load(m_title_id, m_profile, true)) + if (!g_cfg_input.load(m_title_id, m_config_file, true)) { cfg_log.notice("Reloaded empty pad config"); } @@ -1676,36 +1676,36 @@ void pad_settings_dialog::HandleDeviceClassChange(u32 class_id) const } } -void pad_settings_dialog::AddProfile() +void pad_settings_dialog::AddConfigFile() { QInputDialog* dialog = new QInputDialog(this); dialog->setWindowTitle(tr("Choose a unique name")); - dialog->setLabelText(tr("Profile Name: ")); + dialog->setLabelText(tr("Configuration Name: ")); dialog->setFixedSize(500, 100); while (dialog->exec() != QDialog::Rejected) { - const QString profile_name = dialog->textValue(); + const QString config_name = dialog->textValue(); - if (profile_name.isEmpty()) + if (config_name.isEmpty()) { QMessageBox::warning(this, tr("Error"), tr("Name cannot be empty")); continue; } - if (profile_name.contains(".")) + if (config_name.contains(".")) { QMessageBox::warning(this, tr("Error"), tr("Must choose a name without '.'")); continue; } - if (ui->chooseProfile->findText(profile_name) != -1) + if (ui->chooseConfig->findText(config_name) != -1) { QMessageBox::warning(this, tr("Error"), tr("Please choose a non-existing name")); continue; } - if (CreateConfigFile(qstr(rpcs3::utils::get_input_config_dir(m_title_id)), profile_name)) + if (CreateConfigFile(qstr(rpcs3::utils::get_input_config_dir(m_title_id)), config_name)) { - ui->chooseProfile->addItem(profile_name); - ui->chooseProfile->setCurrentText(profile_name); + ui->chooseConfig->addItem(config_name); + ui->chooseConfig->setCurrentText(config_name); } break; } @@ -1864,12 +1864,12 @@ void pad_settings_dialog::SaveExit() } } - const std::string profile_key = m_title_id.empty() ? g_cfg_profile.global_key : m_title_id; + const std::string config_file_key = m_title_id.empty() ? g_cfg_input_configs.global_key : m_title_id; - g_cfg_profile.active_profiles.set_value(profile_key, m_profile); - g_cfg_profile.save(); + g_cfg_input_configs.active_configs.set_value(config_file_key, m_config_file); + g_cfg_input_configs.save(); - g_cfg_input.save(m_title_id, m_profile); + g_cfg_input.save(m_title_id, m_config_file); QDialog::accept(); } @@ -1877,7 +1877,7 @@ void pad_settings_dialog::SaveExit() void pad_settings_dialog::CancelExit() { // Reloads configs from file or defaults - g_cfg_profile.load(); + g_cfg_input_configs.load(); g_cfg_input.from_default(); QDialog::reject(); diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.h b/rpcs3/rpcs3qt/pad_settings_dialog.h index 0be65555a1..2a4f15959f 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.h +++ b/rpcs3/rpcs3qt/pad_settings_dialog.h @@ -72,7 +72,7 @@ class pad_settings_dialog : public QDialog id_reset_parameters, id_blacklist, id_refresh, - id_add_profile, + id_add_config_file, id_ok, id_cancel }; @@ -97,10 +97,10 @@ private Q_SLOTS: void OnTabChanged(int index); void RefreshHandlers(); void ChangeHandler(); - void ChangeProfile(const QString& profile); + void ChangeConfig(const QString& config_file); void ChangeDevice(int index); void HandleDeviceClassChange(u32 class_id) const; - void AddProfile(); + void AddConfigFile(); /** Update the current player config with the GUI values. */ void ApplyCurrentPlayerConfig(int new_player_id); void RefreshPads(); @@ -147,7 +147,7 @@ private: std::mutex m_handler_mutex; std::string m_device_name; std::string m_buddy_device_name; - std::string m_profile; + std::string m_config_file; QTimer m_timer_pad_refresh; int m_last_player_id = 0; diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.ui b/rpcs3/rpcs3qt/pad_settings_dialog.ui index b7c698ced7..bac66e50e4 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.ui +++ b/rpcs3/rpcs3qt/pad_settings_dialog.ui @@ -147,11 +147,11 @@
- + - Profiles + Configuration Files - + 5 @@ -165,12 +165,12 @@ 5 - + - + - Add Profile + Add Configuration false From af850dac99310109821ef17e983a2a74bf351a08 Mon Sep 17 00:00:00 2001 From: Eladash Date: Wed, 30 Aug 2023 20:09:58 +0300 Subject: [PATCH 156/184] Crypto: Fix endianess, avoid crashing on invalid values --- rpcs3/Crypto/unedat.cpp | 82 ++++++++++++++++++++++------------------- rpcs3/Crypto/unself.cpp | 72 +++++++++++++++++------------------- rpcs3/Crypto/utils.h | 29 +-------------- 3 files changed, 80 insertions(+), 103 deletions(-) diff --git a/rpcs3/Crypto/unedat.cpp b/rpcs3/Crypto/unedat.cpp index 74e9bd43af..5ffec82fd8 100644 --- a/rpcs3/Crypto/unedat.cpp +++ b/rpcs3/Crypto/unedat.cpp @@ -135,9 +135,9 @@ std::tuple dec_section(unsigned char* metadata) dec[0x0E] = (metadata[0x6] ^ metadata[0x2] ^ metadata[0x1E]); dec[0x0F] = (metadata[0x7] ^ metadata[0x3] ^ metadata[0x1F]); - u64 offset = swap64(*reinterpret_cast(&dec[0])); - s32 length = swap32(*reinterpret_cast(&dec[8])); - s32 compression_end = swap32(*reinterpret_cast(&dec[12])); + u64 offset = read_from_ptr>(dec, 0); + s32 length = read_from_ptr>(dec, 8); + s32 compression_end = read_from_ptr>(dec, 12); return std::make_tuple(offset, length, compression_end); } @@ -149,7 +149,7 @@ u128 get_block_key(int block, NPD_HEADER *npd) u128 dest_key{}; std::memcpy(&dest_key, src_key, 0xC); - s32 swappedBlock = swap32(block); + s32 swappedBlock = std::bit_cast>(block); std::memcpy(reinterpret_cast(&dest_key) + 0xC, &swappedBlock, sizeof(swappedBlock)); return dest_key; } @@ -193,9 +193,9 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np // NOTE: For NPD version 1 the metadata is not encrypted. if (npd->version <= 1) { - offset = swap64(*reinterpret_cast(&metadata[0x10])); - length = swap32(*reinterpret_cast(&metadata[0x18])); - compression_end = swap32(*reinterpret_cast(&metadata[0x1C])); + offset = read_from_ptr>(metadata, 0x10); + length = read_from_ptr>(metadata, 0x18); + compression_end = read_from_ptr>(metadata, 0x1C); } else { @@ -433,17 +433,26 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs: edat_log.warning("COMPRESSED data detected!"); } - const int block_num = static_cast((edat->file_size + edat->block_size - 1) / edat->block_size); - const int metadata_offset = 0x100; - const int metadata_size = metadata_section_size * block_num; + if (!edat->block_size) + { + return 1; + } + + const usz block_num = utils::aligned_div(edat->file_size, edat->block_size); + constexpr usz metadata_offset = 0x100; + const usz metadata_size = utils::mul_saturate(metadata_section_size, block_num); u64 metadata_section_offset = metadata_offset; - long bytes_read = 0; - long bytes_to_read = metadata_size; - std::unique_ptr metadata(new u8[metadata_size]); - std::unique_ptr empty_metadata(new u8[metadata_size]); + if (utils::add_saturate(utils::add_saturate(file_offset, metadata_section_offset), metadata_size) > f->size()) + { + return 1; + } - while (bytes_to_read > 0) + u64 bytes_read = 0; + const auto metadata = std::make_unique(metadata_size); + const auto empty_metadata = std::make_unique(metadata_size); + + while (bytes_read < metadata_size) { // Locate the metadata blocks. f->seek(file_offset + metadata_section_offset); @@ -453,7 +462,6 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs: // Adjust sizes. bytes_read += metadata_section_size; - bytes_to_read -= metadata_section_size; if (((edat->flags & EDAT_FLAG_0x20) != 0)) // Metadata block before each data block. metadata_section_offset += (metadata_section_size + edat->block_size); @@ -553,18 +561,18 @@ bool validate_dev_klic(const u8* klicensee, NPD_HEADER *npd) return true; } - unsigned char dev[0x60] = { 0 }; + unsigned char dev[0x60]{}; // Build the dev buffer (first 0x60 bytes of NPD header in big-endian). - memcpy(dev, npd, 0x60); + std::memcpy(dev, npd, 0x60); // Fix endianness. - int version = swap32(npd->version); - int license = swap32(npd->license); - int type = swap32(npd->type); - memcpy(dev + 0x4, &version, 4); - memcpy(dev + 0x8, &license, 4); - memcpy(dev + 0xC, &type, 4); + s32 version = std::bit_cast>(npd->version); + s32 license = std::bit_cast>(npd->license); + s32 type = std::bit_cast>(npd->type); + std::memcpy(dev + 0x4, &version, 4); + std::memcpy(dev + 0x8, &license, 4); + std::memcpy(dev + 0xC, &type, 4); // Check for an empty dev_hash (can't validate if devklic is NULL); u128 klic; @@ -638,20 +646,20 @@ void read_npd_edat_header(const fs::file* input, NPD_HEADER& NPD, EDAT_HEADER& E input->read(npd_header, sizeof(npd_header)); input->read(edat_header, sizeof(edat_header)); - memcpy(&NPD.magic, npd_header, 4); - NPD.version = swap32(*reinterpret_cast(&npd_header[4])); - NPD.license = swap32(*reinterpret_cast(&npd_header[8])); - NPD.type = swap32(*reinterpret_cast(&npd_header[12])); - memcpy(NPD.content_id, &npd_header[16], 0x30); - memcpy(NPD.digest, &npd_header[64], 0x10); - memcpy(NPD.title_hash, &npd_header[80], 0x10); - memcpy(NPD.dev_hash, &npd_header[96], 0x10); - NPD.activate_time = swap64(*reinterpret_cast(&npd_header[112])); - NPD.expire_time = swap64(*reinterpret_cast(&npd_header[120])); + std::memcpy(&NPD.magic, npd_header, 4); + NPD.version = read_from_ptr>(npd_header, 4); + NPD.license = read_from_ptr>(npd_header, 8); + NPD.type = read_from_ptr>(npd_header, 12); + std::memcpy(NPD.content_id, &npd_header[16], 0x30); + std::memcpy(NPD.digest, &npd_header[64], 0x10); + std::memcpy(NPD.title_hash, &npd_header[80], 0x10); + std::memcpy(NPD.dev_hash, &npd_header[96], 0x10); + NPD.activate_time = read_from_ptr>(npd_header, 112); + NPD.expire_time = read_from_ptr>(npd_header, 120); - EDAT.flags = swap32(*reinterpret_cast(&edat_header[0])); - EDAT.block_size = swap32(*reinterpret_cast(&edat_header[4])); - EDAT.file_size = swap64(*reinterpret_cast(&edat_header[8])); + EDAT.flags = read_from_ptr>(edat_header, 0); + EDAT.block_size = read_from_ptr>(edat_header, 4); + EDAT.file_size = read_from_ptr>(edat_header, 8); } bool extract_all_data(const fs::file* input, const fs::file* output, const char* input_file_name, unsigned char* devklic, bool verbose) diff --git a/rpcs3/Crypto/unself.cpp b/rpcs3/Crypto/unself.cpp index 16cbf0cb1c..d693ae5abb 100644 --- a/rpcs3/Crypto/unself.cpp +++ b/rpcs3/Crypto/unself.cpp @@ -365,22 +365,14 @@ void MetadataInfo::Show() const void MetadataHeader::Load(u8* in) { - memcpy(&signature_input_length, in, 8); - memcpy(&unknown1, in + 8, 4); - memcpy(§ion_count, in + 12, 4); - memcpy(&key_count, in + 16, 4); - memcpy(&opt_header_size, in + 20, 4); - memcpy(&unknown2, in + 24, 4); - memcpy(&unknown3, in + 28, 4); - // Endian swap. - signature_input_length = swap64(signature_input_length); - unknown1 = swap32(unknown1); - section_count = swap32(section_count); - key_count = swap32(key_count); - opt_header_size = swap32(opt_header_size); - unknown2 = swap32(unknown2); - unknown3 = swap32(unknown3); + signature_input_length = read_from_ptr>(in); + unknown1 = read_from_ptr>(in, 8); + section_count = read_from_ptr>(in, 12); + key_count = read_from_ptr>(in, 16); + opt_header_size = read_from_ptr>(in, 20); + unknown2 = read_from_ptr>(in, 24); + unknown3 = read_from_ptr>(in, 28); } void MetadataHeader::Show() const @@ -396,28 +388,17 @@ void MetadataHeader::Show() const void MetadataSectionHeader::Load(u8* in) { - memcpy(&data_offset, in, 8); - memcpy(&data_size, in + 8, 8); - memcpy(&type, in + 16, 4); - memcpy(&program_idx, in + 20, 4); - memcpy(&hashed, in + 24, 4); - memcpy(&sha1_idx, in + 28, 4); - memcpy(&encrypted, in + 32, 4); - memcpy(&key_idx, in + 36, 4); - memcpy(&iv_idx, in + 40, 4); - memcpy(&compressed, in + 44, 4); - // Endian swap. - data_offset = swap64(data_offset); - data_size = swap64(data_size); - type = swap32(type); - program_idx = swap32(program_idx); - hashed = swap32(hashed); - sha1_idx = swap32(sha1_idx); - encrypted = swap32(encrypted); - key_idx = swap32(key_idx); - iv_idx = swap32(iv_idx); - compressed = swap32(compressed); + data_offset = read_from_ptr>(in); + data_size = read_from_ptr>(in, 8); + type = read_from_ptr>(in, 16); + program_idx = read_from_ptr>(in, 20); + hashed = read_from_ptr>(in, 24); + sha1_idx = read_from_ptr>(in, 28); + encrypted = read_from_ptr>(in, 32); + key_idx = read_from_ptr>(in, 36); + iv_idx = read_from_ptr>(in, 40); + compressed = read_from_ptr>(in, 44); } void MetadataSectionHeader::Show() const @@ -936,19 +917,29 @@ bool SELFDecrypter::LoadHeaders(bool isElf32, SelfAdditionalInfo* out_info) } } - // Read section info. m_seg_ext_hdr.clear(); self_f.seek(m_ext_hdr.segment_ext_hdr_offset); - for(u32 i = 0; i < ((isElf32) ? elf32_hdr.e_phnum : elf64_hdr.e_phnum); ++i) + for(u32 i = 0; i < (isElf32 ? elf32_hdr.e_phnum : elf64_hdr.e_phnum); ++i) { + if (self_f.pos() >= self_f.size()) + { + return false; + } + m_seg_ext_hdr.emplace_back(); m_seg_ext_hdr.back().Load(self_f); } + if (m_ext_hdr.version_hdr_offset == 0 || utils::add_saturate(m_ext_hdr.version_hdr_offset, sizeof(version_header)) > self_f.size()) + { + return false; + } + // Read SCE version info. self_f.seek(m_ext_hdr.version_hdr_offset); + m_version_hdr.Load(self_f); // Read control info. @@ -957,6 +948,11 @@ bool SELFDecrypter::LoadHeaders(bool isElf32, SelfAdditionalInfo* out_info) for (u64 i = 0; i < m_ext_hdr.supplemental_hdr_size;) { + if (self_f.pos() >= self_f.size()) + { + return false; + } + m_supplemental_hdr_arr.emplace_back(); supplemental_header& cinfo = m_supplemental_hdr_arr.back(); cinfo.Load(self_f); diff --git a/rpcs3/Crypto/utils.h b/rpcs3/Crypto/utils.h index 156ba2437b..3e36989dfe 100644 --- a/rpcs3/Crypto/utils.h +++ b/rpcs3/Crypto/utils.h @@ -5,39 +5,12 @@ // http://www.gnu.org/licenses/gpl-2.0.txt #include "util/types.hpp" +#include "util/asm.hpp" #include enum { CRYPTO_MAX_PATH = 4096 }; -// Auxiliary functions (endian swap, xor, and file name). -inline u16 swap16(u16 i) -{ -#if defined(__GNUG__) - return __builtin_bswap16(i); -#else - return _byteswap_ushort(i); -#endif -} - -inline u32 swap32(u32 i) -{ -#if defined(__GNUG__) - return __builtin_bswap32(i); -#else - return _byteswap_ulong(i); -#endif -} - -inline u64 swap64(u64 i) -{ -#if defined(__GNUG__) - return __builtin_bswap64(i); -#else - return _byteswap_uint64(i); -#endif -} - char* extract_file_name(const char* file_path, char real_file_name[CRYPTO_MAX_PATH]); std::string sha256_get_hash(const char* data, usz size, bool lower_case); From 133ddb118c4e1d67085d61e62aee3208c8ddc99f Mon Sep 17 00:00:00 2001 From: kd-11 Date: Thu, 31 Aug 2023 02:08:52 +0300 Subject: [PATCH 157/184] rsx/texture-cache: Remove archaic short-circuit during purge-xxxxx routines --- rpcs3/Emu/RSX/Common/texture_cache_utils.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/rpcs3/Emu/RSX/Common/texture_cache_utils.h b/rpcs3/Emu/RSX/Common/texture_cache_utils.h index 7c3704312c..20f83c0c4b 100644 --- a/rpcs3/Emu/RSX/Common/texture_cache_utils.h +++ b/rpcs3/Emu/RSX/Common/texture_cache_utils.h @@ -472,7 +472,6 @@ namespace rsx block_type blocks[num_blocks]; texture_cache_type *m_tex_cache; std::unordered_set m_in_use; - bool m_purging = false; public: atomic_t m_unreleased_texture_objects = { 0 }; //Number of invalidated objects not yet freed from memory @@ -554,7 +553,6 @@ namespace rsx void purge_unreleased_sections() { - m_purging = true; std::vector textures_to_remove; // Reclaims all graphics memory consumed by dirty textures @@ -579,7 +577,6 @@ namespace rsx tex->destroy(); } - m_purging = false; AUDIT(m_unreleased_texture_objects == 0); } @@ -663,16 +660,12 @@ namespace rsx void on_ranged_block_first_section_created(block_type& block) { - AUDIT(!m_purging); AUDIT(m_in_use.find(&block) == m_in_use.end()); m_in_use.insert(&block); } void on_ranged_block_last_section_destroyed(block_type& block) { - if (m_purging) - return; - AUDIT(m_in_use.find(&block) != m_in_use.end()); m_in_use.erase(&block); } From 1c793edf7d54e8ecd7828072c4b654b9d9e461c6 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Fri, 1 Sep 2023 02:44:15 +0300 Subject: [PATCH 158/184] Fix excluded sections check mismatch --- rpcs3/Emu/RSX/Common/texture_cache.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rpcs3/Emu/RSX/Common/texture_cache.h b/rpcs3/Emu/RSX/Common/texture_cache.h index 7c92dffc8d..676e489967 100644 --- a/rpcs3/Emu/RSX/Common/texture_cache.h +++ b/rpcs3/Emu/RSX/Common/texture_cache.h @@ -60,6 +60,7 @@ namespace rsx std::vector sections_to_unprotect; // These sections are to be unpotected and discarded by caller std::vector sections_to_exclude; // These sections are do be excluded from protection manipulation (subtracted from other sections) u32 num_flushable = 0; + u32 num_excluded = 0; // Sections-to-exclude + sections that would have been excluded but are false positives u64 cache_tag = 0; address_range fault_range; @@ -1013,6 +1014,7 @@ namespace rsx // Do not exclude hashed pages from unprotect! They will cause protection holes result.sections_to_exclude.push_back(&tex); } + result.num_excluded++; continue; } @@ -1100,7 +1102,7 @@ namespace rsx else { // This is a read and all overlapping sections were RO and were excluded (except for cause == superseded_by_fbo) - AUDIT(cause.skip_fbos() || (cause.is_read() && !result.sections_to_exclude.empty())); + AUDIT(cause.skip_fbos() || (cause.is_read() && result.num_excluded > 0)); // We did not handle this violation result.clear_sections(); From f9f2657c984a5e8b2dfeb4e51c47d3727bb9fb13 Mon Sep 17 00:00:00 2001 From: Eladash Date: Fri, 1 Sep 2023 15:48:43 +0300 Subject: [PATCH 159/184] SPU LLVM: Optimize spu_idisable --- rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp | 6 ++++++ rpcs3/Emu/Cell/SPURecompiler.cpp | 13 +++++++++++++ 2 files changed, 19 insertions(+) diff --git a/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp b/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp index c76aba5d4f..1b64c00530 100644 --- a/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp @@ -2607,6 +2607,12 @@ void spu_recompiler::BI(spu_opcode_t op) { spu_log.error("[0x%x] BI: no targets", m_pos); } + else if (op.d && found->second.size() == 1 && found->second[0] == spu_branch_target(m_pos, 1)) + { + // Interrupts-disable pattern + c->mov(SPU_OFF_8(interrupts_enabled), 0); + return; + } c->mov(*addr, SPU_OFF_32(gpr, op.ra, &v128::_u32, 3)); c->and_(*addr, 0x3fffc); diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index 55b5e3c8dc..b9f70ffe31 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -2382,6 +2382,12 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point) spu_log.warning("[0x%x] At 0x%x: indirect branch to 0x%x%s", entry_point, pos, target, op.d ? " (D)" : op.e ? " (E)" : ""); + if (type == spu_itype::BI && target == pos + 4 && op.d) + { + // Disable interrupts idiom + break; + } + m_targets[pos].push_back(target); if (g_cfg.core.spu_block_size == spu_block_size_type::giga) @@ -10870,6 +10876,13 @@ public: // Create jump table if necessary (TODO) const auto tfound = m_targets.find(m_pos); + if (op.d && tfound != m_targets.end() && tfound->second.size() == 1 && tfound->second[0] == spu_branch_target(m_pos, 1)) + { + // Interrupts-disable pattern + m_ir->CreateStore(m_ir->getFalse(), spu_ptr(&spu_thread::interrupts_enabled)); + return; + } + if (!op.d && !op.e && tfound != m_targets.end() && tfound->second.size() > 1) { // Shift aligned address for switch From 7e281a3354aaface59af02fe95836f63727e96dd Mon Sep 17 00:00:00 2001 From: Zion Nimchuk Date: Fri, 1 Sep 2023 11:53:14 -0700 Subject: [PATCH 160/184] Set azure variables --- azure-pipelines.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f19a4e927f..81551d5f18 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -9,6 +9,12 @@ pr: branches: include: - master +variables: + BUILD_REPOSITORY_NAME: $(Build.Repository.Name) + SYSTEM_PULLREQUEST_SOURCEBRANCH: $(System.PullRequest.SourceBranch) + SYSTEM_PULLREQUEST_PULLREQUESTID: $(System.PullRequest.PullRequestId) + BUILD_SOURCEVERSION: $(Build.SourceVersion) + BUILD_SOURCEBRANCHNAME: $(Build.SourceBranchName) jobs: - job: Linux_Build strategy: From d4b1d5c938266d8364a7417eee7c8fc012fe6ac5 Mon Sep 17 00:00:00 2001 From: Zion Nimchuk Date: Fri, 1 Sep 2023 23:09:29 -0700 Subject: [PATCH 161/184] Actually fix build id issue --- Utilities/git-version-gen.cmd | 3 ++- azure-pipelines.yml | 6 ------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/Utilities/git-version-gen.cmd b/Utilities/git-version-gen.cmd index a135055c59..bd524cd760 100644 --- a/Utilities/git-version-gen.cmd +++ b/Utilities/git-version-gen.cmd @@ -91,7 +91,7 @@ for /F %%I IN ('call %GIT% rev-list HEAD --count') do set COMMIT_COUNT=%%I rem // Check if the current build system sets the git branch and version. rem // The name is misleading. This is also used for master builds. -if defined SYSTEM_PULLREQUEST_SOURCEBRANCH ( +if defined BUILD_SOURCEBRANCHNAME ( rem // This must be a CI build @@ -125,6 +125,7 @@ if defined SYSTEM_PULLREQUEST_SOURCEBRANCH ( for /F %%I IN ('call %GIT% rev-parse --short^=8 HEAD') do set GIT_VERSION=%COMMIT_COUNT%-%%I for /F %%I IN ('call %GIT% rev-parse --abbrev-ref HEAD') do set GIT_BRANCH=%%I + set GIT_BRANCH=%BUILD_SOURCEBRANCHNAME% ) else ( rem // This must be a pull request or a build from a fork. echo Assuming pull request build diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 81551d5f18..f19a4e927f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -9,12 +9,6 @@ pr: branches: include: - master -variables: - BUILD_REPOSITORY_NAME: $(Build.Repository.Name) - SYSTEM_PULLREQUEST_SOURCEBRANCH: $(System.PullRequest.SourceBranch) - SYSTEM_PULLREQUEST_PULLREQUESTID: $(System.PullRequest.PullRequestId) - BUILD_SOURCEVERSION: $(Build.SourceVersion) - BUILD_SOURCEBRANCHNAME: $(Build.SourceBranchName) jobs: - job: Linux_Build strategy: From d7b24539801328223b781d36a9a59da4f8c1b6ae Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 2 Sep 2023 10:02:18 +0200 Subject: [PATCH 162/184] VS: Add scripts to filters --- rpcs3/rpcs3.vcxproj | 15 +++++++++++ rpcs3/rpcs3.vcxproj.filters | 51 +++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index dd21034866..afe710087c 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -1893,6 +1893,20 @@ + + + + + + + + + + + + + + @@ -1902,6 +1916,7 @@ + diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index 4de7e725a5..d758cdee03 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -160,6 +160,12 @@ {b927140a-5214-4628-a648-8380ae793179} + + {f4ff05a1-e8c9-44f8-ba05-2c731919a878} + + + {b3bba7ee-4f23-41b6-8ddf-e40f848015de} + @@ -1554,5 +1560,50 @@ StyleSheets + + Scripts + + + CI + + + CI + + + CI + + + CI + + + CI + + + CI + + + CI + + + CI + + + CI + + + CI + + + CI + + + CI + + + CI + + + CI + \ No newline at end of file From 105c694903284333b6e83d97bffbe7edb3769496 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 2 Sep 2023 10:04:52 +0200 Subject: [PATCH 163/184] Windows: update some comments and echos in git-version-gen.cmd --- Utilities/git-version-gen.cmd | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Utilities/git-version-gen.cmd b/Utilities/git-version-gen.cmd index bd524cd760..7de2ebf28f 100644 --- a/Utilities/git-version-gen.cmd +++ b/Utilities/git-version-gen.cmd @@ -90,7 +90,6 @@ rem // Get commit count from (unshallowed) HEAD for /F %%I IN ('call %GIT% rev-list HEAD --count') do set COMMIT_COUNT=%%I rem // Check if the current build system sets the git branch and version. -rem // The name is misleading. This is also used for master builds. if defined BUILD_SOURCEBRANCHNAME ( rem // This must be a CI build @@ -143,8 +142,8 @@ if defined BUILD_SOURCEBRANCHNAME ( ) ) else ( - rem // The name is misleading. This is also used for master builds. - echo SYSTEM_PULLREQUEST_SOURCEBRANCH undefined + echo BUILD_SOURCEBRANCHNAME undefined + echo Assuming local build rem // Make GIT_VERSION the last commit (shortened); Don't include commit count on non-release builds for /F %%I IN ('call %GIT% rev-parse --short^=8 HEAD') do set GIT_VERSION=%%I From a597368c460fde9355ecbb0502eb3872cd54694b Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 30 Aug 2023 23:43:20 +0200 Subject: [PATCH 164/184] SPU: fix some wierd typos (may be wrong, no idea) --- rpcs3/Emu/Cell/PPUModule.cpp | 2 +- rpcs3/Emu/Cell/SPUThread.cpp | 4 ++-- rpcs3/Input/ds4_pad_handler.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index c3bd69ea4b..bfd25b2297 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -1197,7 +1197,7 @@ static void ppu_check_patch_spu_images(const ppu_module& mod, const ppu_segment& { // SPURS GUID pattern end = it; - ppu_log.success("Found SPURS GUID Pattern for terminagtor at 0x%05x", end + seg.addr); + ppu_log.success("Found SPURS GUID Pattern for terminator at 0x%05x", end + seg.addr); break; } diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 45d3f87234..c6fce77462 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -4068,9 +4068,9 @@ bool spu_thread::is_exec_code(u32 addr, std::span ls_ptr, u32 base_add // Test the validity of a single instruction of the optional target // This function can't be too slow and is unlikely to improve results by a great deal const u32 op0 = read_from_ptr>(ls_ptr, route_pc - base_addr); - const auto type0 = s_spu_itype.decode(op); + const spu_itype::type type0 = s_spu_itype.decode(op0); - if (type == spu_itype::UNK || !op) + if (type0 == spu_itype::UNK || !op0) { return false; } diff --git a/rpcs3/Input/ds4_pad_handler.cpp b/rpcs3/Input/ds4_pad_handler.cpp index 0f8c962bd0..a7a4849200 100644 --- a/rpcs3/Input/ds4_pad_handler.cpp +++ b/rpcs3/Input/ds4_pad_handler.cpp @@ -16,8 +16,8 @@ namespace constexpr u32 DS4_GYRO_RES_PER_DEG_S = 86; // technically this could be 1024, but keeping it at 86 keeps us within 16 bits of precision constexpr u32 DS4_FEATURE_REPORT_0x02_SIZE = 37; constexpr u32 DS4_FEATURE_REPORT_0x05_SIZE = 41; - constexpr u32 DS4_FEATURE_REPORT_0x12_SIZE = 16; - constexpr u32 DS4_FEATURE_REPORT_0x81_SIZE = 7; + //constexpr u32 DS4_FEATURE_REPORT_0x12_SIZE = 16; + //constexpr u32 DS4_FEATURE_REPORT_0x81_SIZE = 7; constexpr u32 DS4_FEATURE_REPORT_0xA3_SIZE = 49; constexpr u32 DS4_INPUT_REPORT_0x11_SIZE = 78; constexpr u32 DS4_OUTPUT_REPORT_0x05_SIZE = 32; From e851c044b5f07f8b5dc1a976fafa3fb000118ed8 Mon Sep 17 00:00:00 2001 From: Eladash Date: Thu, 31 Aug 2023 09:54:45 +0300 Subject: [PATCH 165/184] SPU: Function discovery fix Do not detect branch to next. --- rpcs3/Emu/Cell/SPURecompiler.cpp | 12 ++++++------ rpcs3/util/simd.hpp | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index b9f70ffe31..46eda7110e 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -2122,16 +2122,16 @@ std::vector spu_thread::discover_functions(u32 base_addr, std::span(base_addr, 0x10); i < std::min(base_addr + ls.size(), 0x3FFF0); i += 0x10) { - // Search for BRSL and BRASL + // Search for BRSL LR and BRASL LR // TODO: BISL const v128 inst = read_from_ptr>(ls.data(), i - base_addr); - const v128 shifted = gv_shr32(inst, 23); - const v128 eq_brsl = gv_eq32(shifted, v128::from32p(0x66)); - const v128 eq_brasl = gv_eq32(shifted, brasl_mask); + const v128 cleared_i16 = gv_and32(inst, v128::from32p(utils::rol32(~0xffff, 7))); + const v128 eq_brsl = gv_eq32(cleared_i16, v128::from32p(0x66u << 23)); + const v128 eq_brasl = gv_eq32(cleared_i16, brasl_mask); const v128 result = eq_brsl | eq_brasl; if (!gv_testz(result)) @@ -2160,7 +2160,7 @@ std::vector spu_thread::discover_functions(u32 base_addr, std::span> (~c & 7) >> 1) template inline auto gv_fshl8(A&& a, B&& b, C&& c) From 90ad129b83f059816799f97c85be4069132082ef Mon Sep 17 00:00:00 2001 From: Eladash Date: Thu, 31 Aug 2023 11:35:23 +0300 Subject: [PATCH 166/184] Debugger: Fix GOTO and intruction stepping --- rpcs3/Emu/RSX/RSXDisAsm.cpp | 16 ++++++-- rpcs3/Emu/RSX/RSXThread.cpp | 70 +++++++++++++++++++++++---------- rpcs3/Emu/RSX/RSXThread.h | 2 +- rpcs3/rpcs3qt/debugger_list.cpp | 42 ++++++++++++++++++-- 4 files changed, 101 insertions(+), 29 deletions(-) diff --git a/rpcs3/Emu/RSX/RSXDisAsm.cpp b/rpcs3/Emu/RSX/RSXDisAsm.cpp index 7abcc179fe..81ba45c586 100644 --- a/rpcs3/Emu/RSX/RSXDisAsm.cpp +++ b/rpcs3/Emu/RSX/RSXDisAsm.cpp @@ -43,6 +43,11 @@ u32 RSXDisAsm::disasm(u32 pc) if (m_op & RSX_METHOD_NON_METHOD_CMD_MASK) { + if (m_mode == cpu_disasm_mode::survey_cmd_size) + { + return 4; + } + if ((m_op & RSX_METHOD_OLD_JUMP_CMD_MASK) == RSX_METHOD_OLD_JUMP_CMD) { u32 jumpAddr = m_op & RSX_METHOD_OLD_JUMP_OFFSET_MASK; @@ -86,10 +91,13 @@ u32 RSXDisAsm::disasm(u32 pc) } } - if (i == 1) - Write("nop", 0); - else - Write(fmt::format("nop x%u", i), 0); + if (m_mode != cpu_disasm_mode::survey_cmd_size) + { + if (i == 1) + Write("nop", 0); + else + Write(fmt::format("nop x%u", i), 0); + } return i * 4; } diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index 8b1490b7fa..8be464eb13 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -3106,9 +3106,9 @@ namespace rsx fifo_ctrl->invalidate_cache(); } - std::pair thread::try_get_pc_of_x_cmds_backwards(u32 count, u32 get) const + std::pair thread::try_get_pc_of_x_cmds_backwards(s32 count, u32 get) const { - if (!ctrl) + if (!ctrl || state & cpu_flag::exit) { return {0, umax}; } @@ -3124,31 +3124,61 @@ namespace rsx RSXDisAsm disasm(cpu_disasm_mode::survey_cmd_size, vm::g_sudo_addr, 0, this); std::vector pcs_of_valid_cmds; - pcs_of_valid_cmds.reserve(std::min((get - start) / 16, 0x4000)); // Rough estimation of final array size + + if (get > start) + { + pcs_of_valid_cmds.reserve(std::min((get - start) / 16, 0x4000)); // Rough estimation of final array size + } auto probe_code_region = [&](u32 probe_start) -> std::pair { - pcs_of_valid_cmds.clear(); - pcs_of_valid_cmds.push_back(probe_start); - - while (pcs_of_valid_cmds.back() < get) - { - if (u32 advance = disasm.disasm(pcs_of_valid_cmds.back())) - { - pcs_of_valid_cmds.push_back(pcs_of_valid_cmds.back() + advance); - } - else - { - return {0, get}; - } - } - - if (pcs_of_valid_cmds.size() == 1u || pcs_of_valid_cmds.back() != get) + if (probe_start > get) { return {0, get}; } - u32 found_cmds_count = std::min(count, ::size32(pcs_of_valid_cmds) - 1); + pcs_of_valid_cmds.clear(); + pcs_of_valid_cmds.push_back(probe_start); + + usz index_of_get = umax; + usz until = umax; + + while (pcs_of_valid_cmds.size() < until) + { + if (u32 advance = disasm.disasm(pcs_of_valid_cmds.back())) + { + pcs_of_valid_cmds.push_back(utils::add_saturate(pcs_of_valid_cmds.back(), advance)); + } + else + { + break; + } + + if (index_of_get == umax && pcs_of_valid_cmds.back() >= get) + { + index_of_get = pcs_of_valid_cmds.size() - 1; + until = index_of_get + 1; + + if (count < 0 && pcs_of_valid_cmds.back() == get) + { + until -= count; + } + } + } + + if (index_of_get == umax || pcs_of_valid_cmds[index_of_get] != get) + { + return {0, get}; + } + + if (count < 0) + { + const u32 found_cmds_count = std::min(-count, ::size32(pcs_of_valid_cmds) - 1 - index_of_get); + + return {found_cmds_count, pcs_of_valid_cmds[index_of_get + found_cmds_count]}; + } + + const u32 found_cmds_count = std::min(count, ::size32(pcs_of_valid_cmds) - 1); return {found_cmds_count, *(pcs_of_valid_cmds.end() - 1 - found_cmds_count)}; }; diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index 4f378939fd..a1be7e35ec 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -219,7 +219,7 @@ namespace rsx void flush_fifo(); // Returns [count of found commands, PC of their start] - std::pair try_get_pc_of_x_cmds_backwards(u32 count, u32 get) const; + std::pair try_get_pc_of_x_cmds_backwards(s32 count, u32 get) const; void recover_fifo(u32 line = __builtin_LINE(), u32 col = __builtin_COLUMN(), diff --git a/rpcs3/rpcs3qt/debugger_list.cpp b/rpcs3/rpcs3qt/debugger_list.cpp index 011c59146e..d7ce622abe 100644 --- a/rpcs3/rpcs3qt/debugger_list.cpp +++ b/rpcs3/rpcs3qt/debugger_list.cpp @@ -71,8 +71,12 @@ void debugger_list::UpdateCPUData(cpu_thread* cpu, CPUDisAsm* disasm) u32 debugger_list::GetStartAddress(u32 address) { const u32 steps = m_item_count / 3; + const u32 inst_count_jump_on_step = std::min(steps, 4); - u32 result = address; + const bool is_spu = m_cpu && m_cpu->id_type() == 2; + const u32 address_mask = (is_spu ? 0x3fffc : ~3); + + u32 result = address & address_mask; if (m_cpu && m_cpu->id_type() == 0x55) { @@ -83,13 +87,43 @@ u32 debugger_list::GetStartAddress(u32 address) } else { - result = address - (steps * 4); + result = (address - (steps * 4)) & address_mask; } - if (address > m_pc || m_start_addr > address) + u32 upper_bound = (m_start_addr + (steps * 4)) & address_mask; + + if (m_cpu && m_cpu->id_type() == 0x55) + { + if (auto [count, res] = static_cast(m_cpu)->try_get_pc_of_x_cmds_backwards(0 - steps, m_start_addr); count == steps) + { + upper_bound = res; + } + } + + bool goto_addr = false; + + if (upper_bound > m_start_addr) + { + goto_addr = address < m_start_addr || address >= upper_bound; + } + else + { + // Overflowing bounds case + goto_addr = address < m_start_addr && address >= upper_bound; + } + + if (goto_addr) { m_pc = address; - m_start_addr = result; + + if (address > upper_bound && address - upper_bound < inst_count_jump_on_step * 4) + { + m_start_addr = result + inst_count_jump_on_step * 4; + } + else + { + m_start_addr = result; + } } return m_start_addr; From 26b3970485fc29925e1acd728877e1009d1715f2 Mon Sep 17 00:00:00 2001 From: Eladash Date: Thu, 31 Aug 2023 15:29:11 +0300 Subject: [PATCH 167/184] debugger_list: Fix key up/down direction --- rpcs3/rpcs3qt/debugger_list.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rpcs3/rpcs3qt/debugger_list.cpp b/rpcs3/rpcs3qt/debugger_list.cpp index d7ce622abe..002ae1e0bb 100644 --- a/rpcs3/rpcs3qt/debugger_list.cpp +++ b/rpcs3/rpcs3qt/debugger_list.cpp @@ -328,8 +328,8 @@ void debugger_list::keyPressEvent(QKeyEvent* event) { case Qt::Key_PageUp: scroll(0 - m_item_count); return; case Qt::Key_PageDown: scroll(m_item_count); return; - case Qt::Key_Up: scroll(1); return; - case Qt::Key_Down: scroll(-1); return; + case Qt::Key_Up: scroll(-1); return; + case Qt::Key_Down: scroll(1); return; case Qt::Key_I: { if (event->isAutoRepeat()) From a9810ccb7237fa7d124b2bcd5b92cb769d4837d1 Mon Sep 17 00:00:00 2001 From: Eladash Date: Fri, 1 Sep 2023 15:07:46 +0300 Subject: [PATCH 168/184] SPU LLVM: Another fix for Game Collection's precompilation --- Utilities/lockless.h | 41 ++++++++++++++++++++++++++++++++ rpcs3/Emu/Cell/PPUThread.cpp | 7 ++++++ rpcs3/Emu/Cell/SPURecompiler.cpp | 40 ++++++------------------------- rpcs3/Emu/Cell/SPURecompiler.h | 11 +++++++++ 4 files changed, 66 insertions(+), 33 deletions(-) diff --git a/Utilities/lockless.h b/Utilities/lockless.h index 87e726ee3c..f49544ea3d 100644 --- a/Utilities/lockless.h +++ b/Utilities/lockless.h @@ -270,6 +270,30 @@ public: return {}; } + const T& operator[](usz index) const noexcept + { + lf_queue_iterator result = begin(); + + while (--index != umax) + { + result++; + } + + return *result; + } + + T& operator[](usz index) noexcept + { + lf_queue_iterator result = begin(); + + while (--index != umax) + { + result++; + } + + return *result; + } + lf_queue_slice& pop_front() { delete std::exchange(m_head, std::exchange(m_head->m_link, nullptr)); @@ -315,6 +339,23 @@ class lf_queue final public: constexpr lf_queue() = default; + lf_queue(lf_queue&& other) noexcept + { + m_head.release(other.m_head.exchange(0)); + } + + lf_queue& operator=(lf_queue&& other) noexcept + { + if (this == std::addressof(other)) + { + return *this; + } + + delete load(m_head); + m_head.release(other.m_head.exchange(0)); + return *this; + } + ~lf_queue() { delete load(m_head); diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 8b53f4ca33..0878feb511 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -3907,6 +3907,8 @@ extern void ppu_precompile(std::vector& dir_queue, std::vectorget(); _main = {}; + auto current_cache = std::move(g_fxo->get()); + if (!ppu_load_exec(obj, true, path)) { // Abort @@ -3916,11 +3918,13 @@ extern void ppu_precompile(std::vector& dir_queue, std::vectorget() = std::move(current_cache); break; } if (!_main.analyse(0, _main.elf_entry, _main.seg0_code_end, _main.applied_pathes, [](){ return Emu.IsStopped(); })) { + g_fxo->get() = std::move(current_cache); break; } @@ -3929,8 +3933,10 @@ extern void ppu_precompile(std::vector& dir_queue, std::vectorget() = std::move(current_cache); break; } @@ -3945,6 +3951,7 @@ extern void ppu_precompile(std::vector& dir_queue, std::vectorget() = std::move(main_module); + g_fxo->get().collect_funcs_to_precompile = true; Emu.ConfigurePPUCache(); }); diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index 46eda7110e..28e2f330c5 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -516,20 +516,6 @@ spu_cache::~spu_cache() { } -struct spu_section_data -{ - struct data_t - { - u32 vaddr; - std::basic_string inst_data; - std::vector funcs; - }; - - shared_mutex mtx; - atomic_t had_been_used = false; - std::vector data; -}; - extern void utilize_spu_data_segment(u32 vaddr, const void* ls_data_vaddr, u32 size) { if (vaddr % 4) @@ -549,9 +535,9 @@ extern void utilize_spu_data_segment(u32 vaddr, const void* ls_data_vaddr, u32 s return; } - g_fxo->need(); + g_fxo->need(); - if (g_fxo->get().had_been_used) + if (!g_fxo->get().collect_funcs_to_precompile) { return; } @@ -559,7 +545,7 @@ extern void utilize_spu_data_segment(u32 vaddr, const void* ls_data_vaddr, u32 s std::basic_string data(size / 4, 0); std::memcpy(data.data(), ls_data_vaddr, size); - spu_section_data::data_t obj{vaddr, std::move(data)}; + spu_cache::precompile_data_t obj{vaddr, std::move(data)}; obj.funcs = spu_thread::discover_functions(vaddr, { reinterpret_cast(ls_data_vaddr), size }, vaddr != 0, umax); @@ -576,19 +562,7 @@ extern void utilize_spu_data_segment(u32 vaddr, const void* ls_data_vaddr, u32 s spu_log.notice("Found %u SPU functions", obj.funcs.size()); - std::lock_guard lock(g_fxo->get().mtx); - - for (const auto& data : g_fxo->get().data) - { - // TODO: More robust duplicates filtering - if (data.vaddr == vaddr && data.inst_data.starts_with(obj.inst_data)) - { - spu_log.notice("Avoided duplicate SPU segment"); - return; - } - } - - g_fxo->get().data.emplace_back(std::move(obj)); + g_fxo->get().precompile_funcs.push(std::move(obj)); } std::deque spu_cache::get() @@ -693,8 +667,8 @@ void spu_cache::initialize(bool build_existing_cache) atomic_t fnext{}; atomic_t fail_flag{0}; - auto data_list = std::move(g_fxo->get().data); - g_fxo->get().had_been_used = true; + auto data_list = g_fxo->get().precompile_funcs.pop_all(); + g_fxo->get().collect_funcs_to_precompile = false; u32 total_precompile = 0; @@ -717,7 +691,7 @@ void spu_cache::initialize(bool build_existing_cache) else { total_precompile = 0; - data_list.clear(); + data_list = {}; } atomic_t data_indexer = 0; diff --git a/rpcs3/Emu/Cell/SPURecompiler.h b/rpcs3/Emu/Cell/SPURecompiler.h index 5dd68184b1..dbe56e9ff1 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.h +++ b/rpcs3/Emu/Cell/SPURecompiler.h @@ -35,6 +35,17 @@ public: void add(const struct spu_program& func); static void initialize(bool build_existing_cache = true); + + struct precompile_data_t + { + u32 vaddr; + std::basic_string inst_data; + std::vector funcs; + }; + + bool collect_funcs_to_precompile = true; + + lf_queue precompile_funcs; }; struct spu_program From a626ccfcad5b63cacbc0abe4686b81bc8e3f28d0 Mon Sep 17 00:00:00 2001 From: Eladash Date: Fri, 1 Sep 2023 19:38:06 +0300 Subject: [PATCH 169/184] SPU LLVM: Initial precompilation of tail-calls --- rpcs3/Emu/Cell/SPUAnalyser.h | 34 +++++++----- rpcs3/Emu/Cell/SPUDisAsm.h | 7 +++ rpcs3/Emu/Cell/SPURecompiler.cpp | 88 +++++++++++++++++++++++++++++++- rpcs3/Emu/Cell/SPUThread.cpp | 10 ++++ 4 files changed, 124 insertions(+), 15 deletions(-) diff --git a/rpcs3/Emu/Cell/SPUAnalyser.h b/rpcs3/Emu/Cell/SPUAnalyser.h index d448fca473..1d33fc7156 100644 --- a/rpcs3/Emu/Cell/SPUAnalyser.h +++ b/rpcs3/Emu/Cell/SPUAnalyser.h @@ -14,12 +14,13 @@ struct spu_itype static constexpr struct floating_tag{} floating{}; // Floating-Point Instructions static constexpr struct quadrop_tag{} _quadrop{}; // 4-op Instructions static constexpr struct xfloat_tag{} xfloat{}; // Instructions producing xfloat values + static constexpr struct zregmod_tag{} zregmod{}; // Instructions not modifying any GPR enum type : unsigned char { UNK = 0, - HEQ, + HEQ, // zregmod_tag first HEQI, HGT, HGTI, @@ -36,11 +37,21 @@ struct spu_itype NOP, SYNC, DSYNC, - MFSPR, MTSPR, + WRCH, + + STQD, // memory_tag first + STQX, + STQA, + STQR, // zregmod_tag last + LQD, + LQX, + LQA, + LQR, // memory_tag last + + MFSPR, RDCH, RCHCNT, - WRCH, BR, // branch_tag first BRA, @@ -59,15 +70,6 @@ struct spu_itype BIHZ, BIHNZ, // branch_tag last - LQD, // memory_tag first - LQX, - LQA, - LQR, - STQD, - STQX, - STQA, - STQR, // memory_tag last - ILH, // constant_tag_first ILHU, IL, @@ -267,7 +269,7 @@ struct spu_itype // Test for memory instruction friend constexpr bool operator &(type value, memory_tag) { - return value >= LQD && value <= STQR; + return value >= STQD && value <= LQR; } // Test for compare instruction @@ -293,6 +295,12 @@ struct spu_itype { return value >= ILH && value <= FSMBI; } + + // Test for non register-modifying instruction + friend constexpr bool operator &(type value, zregmod_tag) + { + return value >= HEQ && value <= STQR; + } }; struct spu_iflag diff --git a/rpcs3/Emu/Cell/SPUDisAsm.h b/rpcs3/Emu/Cell/SPUDisAsm.h index e891ed6fcc..0d5862025b 100644 --- a/rpcs3/Emu/Cell/SPUDisAsm.h +++ b/rpcs3/Emu/Cell/SPUDisAsm.h @@ -851,6 +851,13 @@ public: } void BR(spu_opcode_t op) { + if (op.rt && op.rt != 127u) + { + // Valid but makes no sense + DisAsm("br??", DisAsmBranchTarget(op.i16)); + return; + } + DisAsm("br", DisAsmBranchTarget(op.i16)); } void FSMBI(spu_opcode_t op) diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index 28e2f330c5..7ca2143bd2 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -2091,21 +2091,25 @@ void spu_recompiler_base::old_interpreter(spu_thread& spu, void* ls, u8* /*rip*/ std::vector spu_thread::discover_functions(u32 base_addr, std::span ls, bool is_known_addr, u32 /*entry*/) { std::vector calls; + std::vector branches; + calls.reserve(100); // Discover functions // Use the most simple method: search for instructions that calls them - // And then filter invalid cases (does not detect tail calls) + // And then filter invalid cases + // TODO: Does not detect jumptables or fixed-addr indirect calls const v128 brasl_mask = is_known_addr ? v128::from32p(0x62u << 23) : v128::from32p(umax); for (u32 i = utils::align(base_addr, 0x10); i < std::min(base_addr + ls.size(), 0x3FFF0); i += 0x10) { - // Search for BRSL LR and BRASL LR + // Search for BRSL LR and BRASL LR or BR // TODO: BISL const v128 inst = read_from_ptr>(ls.data(), i - base_addr); const v128 cleared_i16 = gv_and32(inst, v128::from32p(utils::rol32(~0xffff, 7))); const v128 eq_brsl = gv_eq32(cleared_i16, v128::from32p(0x66u << 23)); const v128 eq_brasl = gv_eq32(cleared_i16, brasl_mask); + const v128 eq_br = gv_eq32(cleared_i16, v128::from32p(0x64u << 23)); const v128 result = eq_brsl | eq_brasl; if (!gv_testz(result)) @@ -2118,6 +2122,17 @@ std::vector spu_thread::discover_functions(u32 base_addr, std::span spu_thread::discover_functions(u32 base_addr, std::span addrs; for (u32 addr : calls) @@ -2142,6 +2163,69 @@ std::vector spu_thread::discover_functions(u32 base_addr, std::span>(ls, addr - base_addr)}; + + const u32 func = op_branch_targets(addr, op)[0]; + + if (func == umax || addr + 4 == func || func == addr || !addr) + { + continue; + } + + // Search for AI R1, +x or OR R3/4, Rx, 0 + // Reasoning: AI R1, +x means stack pointer restoration, branch after that is likely a tail call + // R3 and R4 are common function arguments because they are the first two + for (u32 back = addr - 4, it = 5; it && back >= base_addr; back -= 4) + { + const spu_opcode_t test_op{read_from_ptr>(ls, back - base_addr)}; + const auto type = g_spu_itype.decode(test_op.opcode); + + if (type & spu_itype::branch) + { + break; + } + + bool is_tail = false; + + if (type == spu_itype::AI && test_op.rt == 1u && test_op.ra == 1u) + { + if (test_op.si10 <= 0) + { + break; + } + + is_tail = true; + } + else if (!(type & spu_itype::zregmod)) + { + const u32 op_rt = type & spu_itype::_quadrop ? +test_op.rt4 : +test_op.rt; + + if (op_rt >= 80u && (type != spu_itype::LQD || test_op.ra != 1u)) + { + // Modifying non-volatile registers, not a call (and not context restoration) + break; + } + + //is_tail = op_rt == 3u || op_rt == 4u; + } + + if (!is_tail) + { + continue; + } + + if (std::count(addrs.begin(), addrs.end(), func)) + { + break; + } + + addrs.push_back(func); + break; + } + } + std::sort(addrs.begin(), addrs.end()); return addrs; diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index c6fce77462..6daba511b4 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -4042,8 +4042,18 @@ bool spu_thread::is_exec_code(u32 addr, std::span ls_ptr, u32 base_add return false; } + if (type == spu_itype::STOP && op.rb) + { + return false; + } + if (type & spu_itype::branch) { + if (type == spu_itype::BR && op.rt && op.rt != 127u) + { + return false; + } + const auto results = op_branch_targets(addr, spu_opcode_t{op}); if (results[0] == umax) From 7c0d8fc29ca2a09a1735de7485ab9cd839d0e745 Mon Sep 17 00:00:00 2001 From: Eladash Date: Fri, 1 Sep 2023 21:40:05 +0300 Subject: [PATCH 170/184] Improve spu_thread::is_exec_code --- rpcs3/Emu/Cell/SPUThread.cpp | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 6daba511b4..9531304fdf 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -4034,10 +4034,10 @@ bool spu_thread::is_exec_code(u32 addr, std::span ls_ptr, u32 base_add } const u32 addr0 = spu_branch_target(addr); - const u32 op = read_from_ptr>(ls_ptr, addr0 - base_addr); - const auto type = s_spu_itype.decode(op); + const spu_opcode_t op{read_from_ptr>(ls_ptr, addr0 - base_addr)}; + const auto type = s_spu_itype.decode(op.opcode); - if (type == spu_itype::UNK || !op) + if (type == spu_itype::UNK || !op.opcode) { return false; } @@ -4054,11 +4054,30 @@ bool spu_thread::is_exec_code(u32 addr, std::span ls_ptr, u32 base_add return false; } - const auto results = op_branch_targets(addr, spu_opcode_t{op}); + auto results = op_branch_targets(addr, op); if (results[0] == umax) { - break; + switch (type) + { + case spu_itype::BIZ: + case spu_itype::BINZ: + case spu_itype::BIHZ: + case spu_itype::BIHNZ: + { + results[0] = addr + 4; + break; + } + default: + { + break; + } + } + + if (results[0] == umax) + { + break; + } } for (usz res_i = 1; res_i < results.size(); res_i++) From 5e110f2844eb13ec64d8471c629291733b0ccffc Mon Sep 17 00:00:00 2001 From: Eladash Date: Fri, 1 Sep 2023 22:21:47 +0300 Subject: [PATCH 171/184] SPU LLVM: Try to precompile filler-sapce between functions --- rpcs3/Emu/Cell/SPURecompiler.cpp | 48 ++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index 7ca2143bd2..a3a35d5535 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -842,6 +842,7 @@ void spu_cache::initialize(bool build_existing_cache) { u32 passed_count = 0; u32 func_addr = 0; + u32 next_func = 0; u32 sec_addr = umax; u32 sec_idx = 0; std::basic_string_view inst_data; @@ -853,6 +854,7 @@ void spu_cache::initialize(bool build_existing_cache) { sec_addr = sec.vaddr; func_addr = ::at32(sec.funcs, func_i - passed_count); + next_func = ::at32(sec.funcs, std::min(func_i - passed_count + 1, sec.funcs.size() - 1)); inst_data = sec.inst_data; break; } @@ -907,18 +909,12 @@ void spu_cache::initialize(bool build_existing_cache) result++; - if (g_cfg.core.spu_block_size >= spu_block_size_type::mega) - { - // Should already take care of the entire function - break; - } + u32 start_new = func_addr + prog_size * 4; if (auto type = g_spu_itype.decode(last_inst); type == spu_itype::BRSL || type == spu_itype::BRASL || type == spu_itype::BISL) { - const u32 start_new = func_addr + prog_size * 4; - - if (start_new < SPU_LS_SIZE && ls[start_new / 4] && g_spu_itype.decode(ls[start_new / 4]) != spu_itype::UNK) + if (start_new < SPU_LS_SIZE && start_new != next_func && ls[start_new / 4] && g_spu_itype.decode(ls[start_new / 4]) != spu_itype::UNK) { spu_log.notice("Precompiling fallthrough to 0x%05x", start_new); func2 = compiler->analyse(ls.data(), start_new); @@ -927,6 +923,42 @@ void spu_cache::initialize(bool build_existing_cache) } } + // Disabled + if (0 && next_func > func_addr && start_new < next_func) + { + if (auto type = g_spu_itype.decode(ls[start_new / 4]); start_new % 8 && (type == spu_itype::LNOP || type == spu_itype::NOP)) + { + start_new += 4; + + if (start_new == next_func) + { + break; + } + } + + if (!spu_thread::is_exec_code(start_new, { reinterpret_cast(ls.data()), SPU_LS_SIZE }, 0)) + { + break; + } + + spu_log.notice("Precompiling filler space at 0x%05x", start_new); + func2 = compiler->analyse(ls.data(), start_new); + + if (start_new + func2.data.size() * 4 > next_func) + { + break; + } + + while (func2.data.size() == 1 && start_new + 4 < next_func) + { + start_new += 4; + func2 = compiler->analyse(ls.data(), start_new); + } + + func_addr = start_new; + continue; + } + break; } } From deacf76ca452c748fc8cf891d1fa91f76b13c8f3 Mon Sep 17 00:00:00 2001 From: Eladash Date: Sat, 2 Sep 2023 09:01:35 +0300 Subject: [PATCH 172/184] PPU Analyzer: Revert TRAP detection change --- rpcs3/Emu/Cell/PPUAnalyser.cpp | 2 +- rpcs3/Emu/Cell/PPUThread.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUAnalyser.cpp b/rpcs3/Emu/Cell/PPUAnalyser.cpp index 2968190f41..074b2dd89e 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.cpp +++ b/rpcs3/Emu/Cell/PPUAnalyser.cpp @@ -1397,7 +1397,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b } else if (type & ppu_itype::trap) { - if (op.bo != 31) + if (op.opcode != ppu_instructions::TRAP()) { add_block(_ptr.addr()); } diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 0878feb511..d9144a2d5b 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -3681,7 +3681,7 @@ extern void ppu_precompile(std::vector& dir_queue, std::vector Date: Sat, 2 Sep 2023 09:29:58 +0300 Subject: [PATCH 173/184] SPU LLVM: Fix crashes on corrupted cache file * Fix OOM if size is too high. * Fix out-of-bounds access beyond SPU_LS_SIZE. --- rpcs3/Emu/Cell/SPURecompiler.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index a3a35d5535..fd213b1247 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -588,9 +588,12 @@ std::deque spu_cache::get() break; } - func.resize(size); + if (utils::add_saturate(addr, utils::mul_saturate(size, 4)) > SPU_LS_SIZE) + { + break; + } - if (m_file.read(func.data(), func.size() * 4) != func.size() * 4) + if (!m_file.read(func, size)) { break; } From 66b6bae59689bba3f4928335eb3dafcfaf77ec0d Mon Sep 17 00:00:00 2001 From: Eladash Date: Sat, 2 Sep 2023 11:01:38 +0300 Subject: [PATCH 174/184] Win32/File.cpp: Avoid potential race in concurrent writes Uninitialized data is better than overwritten data. Affects SPU Cache (unprotected writes). --- Utilities/File.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Utilities/File.cpp b/Utilities/File.cpp index 4f0094d23d..4d1d9acd2a 100644 --- a/Utilities/File.cpp +++ b/Utilities/File.cpp @@ -1303,12 +1303,12 @@ fs::file::file(const std::string& path, bs_t mode) DWORD nwritten = 0; OVERLAPPED ovl{}; - const u64 pos = m_pos; + const u64 pos = m_pos.fetch_add(size); ovl.Offset = DWORD(pos); ovl.OffsetHigh = DWORD(pos >> 32); ensure(WriteFile(m_handle, data, size, &nwritten, &ovl)); // "file::write" + ensure(nwritten == size); nwritten_sum += nwritten; - m_pos += nwritten; if (nwritten < size) { From dd4840caf6af3a7adeb22a562af60ec1911ce413 Mon Sep 17 00:00:00 2001 From: Eladash Date: Sat, 2 Sep 2023 11:55:21 +0300 Subject: [PATCH 175/184] SPU LLVM: Add CRC check for cache --- rpcs3/Emu/Cell/SPURecompiler.cpp | 58 +++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index fd213b1247..0ef9903be1 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -565,6 +565,21 @@ extern void utilize_spu_data_segment(u32 vaddr, const void* ls_data_vaddr, u32 s g_fxo->get().precompile_funcs.push(std::move(obj)); } +// For SPU cache validity check +static u16 calculate_crc16(const uchar* data, usz length) +{ + u16 crc = umax; + + while (length--) + { + u8 x = (crc >> 8) ^ *data++; + x ^= (x >> 4); + crc = static_cast((crc << 8) ^ (x << 12) ^ (x << 5) ^ x); + } + + return crc; +} + std::deque spu_cache::get() { std::deque result; @@ -579,20 +594,29 @@ std::deque spu_cache::get() // TODO: signal truncated or otherwise broken file while (true) { - be_t size; - be_t addr; + struct block_info_t + { + be_t crc; + be_t size; + be_t addr; + } block_info{}; + + if (!m_file.read(block_info)) + { + break; + } + + const u32 crc = block_info.crc; + const u32 size = block_info.size; + const u32 addr = block_info.addr; + + if (utils::add_saturate(addr, size * 4) > SPU_LS_SIZE) + { + break; + } + std::vector func; - if (!m_file.read(size) || !m_file.read(addr)) - { - break; - } - - if (utils::add_saturate(addr, utils::mul_saturate(size, 4)) > SPU_LS_SIZE) - { - break; - } - if (!m_file.read(func, size)) { break; @@ -604,6 +628,13 @@ std::deque spu_cache::get() continue; } + // CRC check is optional to be compatible with old format + if (crc && std::max(calculate_crc16(reinterpret_cast(func.data()), size * 4), 1) != crc) + { + // Invalid, but continue anyway + continue; + } + spu_program res; res.entry_point = addr; res.lower_bound = addr; @@ -624,6 +655,9 @@ void spu_cache::add(const spu_program& func) be_t size = ::size32(func.data); be_t addr = func.entry_point; + // Add CRC (forced non-zero) + size |= std::max(calculate_crc16(reinterpret_cast(func.data.data()), size * 4), 1) << 16; + const fs::iovec_clone gather[3] { {&size, sizeof(size)}, From 11006dac35544f3fd990fb7c6c6fc1a0e313d26a Mon Sep 17 00:00:00 2001 From: Eladash Date: Fri, 1 Sep 2023 03:01:12 +0300 Subject: [PATCH 176/184] sys_rsx: Fix local memory size reported in driver info --- rpcs3/Emu/Cell/lv2/sys_rsx.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/rpcs3/Emu/Cell/lv2/sys_rsx.cpp b/rpcs3/Emu/Cell/lv2/sys_rsx.cpp index 1785043d6f..7479da7f99 100644 --- a/rpcs3/Emu/Cell/lv2/sys_rsx.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_rsx.cpp @@ -177,6 +177,12 @@ error_code sys_rsx_memory_allocate(cpu_thread& cpu, vm::ptr mem_handle, vm: if (vm::falloc(rsx::constants::local_mem_base, size, vm::video)) { rsx::get_current_renderer()->local_mem_size = size; + + if (u32 addr = rsx::get_current_renderer()->driver_info) + { + vm::_ref(addr).memory_size = size; + } + *mem_addr = rsx::constants::local_mem_base; *mem_handle = 0x5a5a5a5b; return CELL_OK; @@ -285,6 +291,7 @@ error_code sys_rsx_context_allocate(cpu_thread& cpu, vm::ptr context_id, vm driverInfo.version_driver = 0x211; driverInfo.version_gpu = 0x5c; + driverInfo.memory_size = render->local_mem_size; driverInfo.nvcore_frequency = 500000000; // 0x1DCD6500 driverInfo.memory_frequency = 650000000; // 0x26BE3680 driverInfo.reportsNotifyOffset = 0x1000; From ea579849126426a38467a27473f47e5a250bd5b6 Mon Sep 17 00:00:00 2001 From: Elad Ashkenazi Date: Sat, 2 Sep 2023 15:56:34 +0300 Subject: [PATCH 177/184] SPU LLVM: Fill space between functions using targets (Precompilation) * Revert "PPU Analyzer: Revert TRAP detection change" --- rpcs3/Emu/Cell/PPUAnalyser.cpp | 2 +- rpcs3/Emu/Cell/SPURecompiler.cpp | 106 +++++++++++++++++++------------ rpcs3/Emu/Cell/SPURecompiler.h | 5 ++ 3 files changed, 71 insertions(+), 42 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUAnalyser.cpp b/rpcs3/Emu/Cell/PPUAnalyser.cpp index 074b2dd89e..2968190f41 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.cpp +++ b/rpcs3/Emu/Cell/PPUAnalyser.cpp @@ -1397,7 +1397,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b } else if (type & ppu_itype::trap) { - if (op.opcode != ppu_instructions::TRAP()) + if (op.bo != 31) { add_block(_ptr.addr()); } diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index 0ef9903be1..62cf32f2b2 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -889,9 +889,10 @@ void spu_cache::initialize(bool build_existing_cache) { if (func_i < passed_count + sec.funcs.size()) { + const u32 func_idx = func_i - passed_count; sec_addr = sec.vaddr; - func_addr = ::at32(sec.funcs, func_i - passed_count); - next_func = ::at32(sec.funcs, std::min(func_i - passed_count + 1, sec.funcs.size() - 1)); + func_addr = ::at32(sec.funcs, func_idx); + next_func = sec.funcs.size() >= func_idx + 1 ? SPU_LS_SIZE : sec.funcs[func_idx]; inst_data = sec.inst_data; break; } @@ -929,8 +930,12 @@ void spu_cache::initialize(bool build_existing_cache) last_sec_idx = sec_idx; } + u32 block_addr = func_addr; + // Call analyser - spu_program func2 = compiler->analyse(ls.data(), func_addr); + spu_program func2 = compiler->analyse(ls.data(), block_addr); + + std::map> targets; while (!func2.data.empty()) { @@ -946,57 +951,76 @@ void spu_cache::initialize(bool build_existing_cache) result++; - u32 start_new = func_addr + prog_size * 4; + const u32 start_new = block_addr + prog_size * 4; + + if (start_new >= next_func || (start_new == next_func - 4 && ls[start_new / 4] == 0x200000u)) + { + // Completed + break; + } + + targets.insert(compiler->get_targets().begin(), compiler->get_targets().end()); if (auto type = g_spu_itype.decode(last_inst); - type == spu_itype::BRSL || type == spu_itype::BRASL || type == spu_itype::BISL) + type == spu_itype::BRSL || type == spu_itype::BRASL || type == spu_itype::BISL || type == spu_itype::SYNC) { - if (start_new < SPU_LS_SIZE && start_new != next_func && ls[start_new / 4] && g_spu_itype.decode(ls[start_new / 4]) != spu_itype::UNK) + if (ls[start_new / 4] && g_spu_itype.decode(ls[start_new / 4]) != spu_itype::UNK) { spu_log.notice("Precompiling fallthrough to 0x%05x", start_new); func2 = compiler->analyse(ls.data(), start_new); - func_addr = start_new; + block_addr = start_new; continue; } } - // Disabled - if (0 && next_func > func_addr && start_new < next_func) + if (targets.empty()) { - if (auto type = g_spu_itype.decode(ls[start_new / 4]); start_new % 8 && (type == spu_itype::LNOP || type == spu_itype::NOP)) - { - start_new += 4; - - if (start_new == next_func) - { - break; - } - } - - if (!spu_thread::is_exec_code(start_new, { reinterpret_cast(ls.data()), SPU_LS_SIZE }, 0)) - { - break; - } - - spu_log.notice("Precompiling filler space at 0x%05x", start_new); - func2 = compiler->analyse(ls.data(), start_new); - - if (start_new + func2.data.size() * 4 > next_func) - { - break; - } - - while (func2.data.size() == 1 && start_new + 4 < next_func) - { - start_new += 4; - func2 = compiler->analyse(ls.data(), start_new); - } - - func_addr = start_new; - continue; + break; } - break; + const auto upper = targets.upper_bound(func_addr); + + if (upper == targets.begin()) + { + break; + } + + u32 new_entry = umax; + + // Find the lowest target in the space in-between + for (auto it = std::prev(upper); it != targets.end() && it->first < start_new && new_entry > start_new; it++) + { + for (u32 target : it->second) + { + if (target >= start_new && target < next_func) + { + if (target < new_entry) + { + new_entry = target; + + if (new_entry == start_new) + { + // Cannot go lower + break; + } + } + } + } + } + + if (new_entry == umax) + { + break; + } + + if (!spu_thread::is_exec_code(new_entry, { reinterpret_cast(ls.data()), SPU_LS_SIZE })) + { + break; + } + + spu_log.notice("Precompiling filler space at 0x%05x (next=0x%05x)", new_entry, next_func); + func2 = compiler->analyse(ls.data(), new_entry); + block_addr = new_entry; } } diff --git a/rpcs3/Emu/Cell/SPURecompiler.h b/rpcs3/Emu/Cell/SPURecompiler.h index dbe56e9ff1..02870a4105 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.h +++ b/rpcs3/Emu/Cell/SPURecompiler.h @@ -342,6 +342,11 @@ public: return *m_spurt; } + const auto& get_targets() const + { + return m_targets; + } + // Create recompiler instance (ASMJIT) static std::unique_ptr make_asmjit_recompiler(); From 150afecc2936d3584836d48e7b77189e19ecaabf Mon Sep 17 00:00:00 2001 From: Eladash Date: Sat, 2 Sep 2023 19:26:21 +0300 Subject: [PATCH 178/184] Fixup spu_thread::discover_functions --- rpcs3/Emu/Cell/SPURecompiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index 62cf32f2b2..2f7bcfbf0f 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -2270,7 +2270,7 @@ std::vector spu_thread::discover_functions(u32 base_addr, std::span= base_addr; back -= 4) + for (u32 back = addr - 4, it = 10; it && back >= base_addr && back < std::min(base_addr + ls.size(), 0x3FFF0); it--, back -= 4) { const spu_opcode_t test_op{read_from_ptr>(ls, back - base_addr)}; const auto type = g_spu_itype.decode(test_op.opcode); From d62d6cc85271fb0c786f9854c366ee452be47f0c Mon Sep 17 00:00:00 2001 From: Eladash Date: Sat, 2 Sep 2023 19:42:37 +0300 Subject: [PATCH 179/184] Progress Dialog: Force-update counter when complete This confuses both the user and the developer at times. --- rpcs3/Emu/system_progress.cpp | 45 +++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/rpcs3/Emu/system_progress.cpp b/rpcs3/Emu/system_progress.cpp index f829944c39..b2be9e84e8 100644 --- a/rpcs3/Emu/system_progress.cpp +++ b/rpcs3/Emu/system_progress.cpp @@ -156,7 +156,7 @@ void progress_dialog_server::operator()() u32 fdone = 0; u32 ptotal = 0; u32 pdone = 0; - auto text1 = text0; + const char* text1 = nullptr; const u64 start_time = get_system_time(); @@ -171,11 +171,17 @@ void progress_dialog_server::operator()() fdone = fdone_new; ptotal = ptotal_new; pdone = pdone_new; - text1 = text_new; - if (!text_new) + const bool text_changed = text_new && text_new != text1; + + if (text_new) { - // Incomplete state + text1 = text_new; + } + + if (!text1) + { + // Cannot do anything continue; } @@ -202,7 +208,7 @@ void progress_dialog_server::operator()() // Assume not all programs were found if files were not compiled (as it may contain more) const u64 total = std::max(ptotal, 1) * std::max(ftotal, 1); const u64 done = pdone * std::max(fdone, 1); - const f32 value = static_cast(std::fmin(done * 100. / total, 100.f)); + const u32 value = static_cast(done >= total ? 100 : done * 100 / total); std::string progr = "Progress:"; @@ -214,26 +220,28 @@ void progress_dialog_server::operator()() // Changes detected, send update if (native_dlg) { - native_dlg->set_text(text_new); + if (text_changed) + { + native_dlg->set_text(text1); + } + native_dlg->progress_bar_set_message(0, std::move(progr)); - native_dlg->progress_bar_set_value(0, std::floor(value)); + native_dlg->progress_bar_set_value(0, static_cast(value)); } else if (dlg) { Emu.CallFromMainThread([=]() { - dlg->SetMsg(text_new); + if (text_changed) + { + dlg->SetMsg(text1); + } + dlg->ProgressBarSetMsg(0, progr); - dlg->ProgressBarSetValue(0, static_cast(std::floor(value))); + dlg->ProgressBarSetValue(0, value); }); } } - // Leave only if total count is equal to done count - else if (ftotal == fdone && ptotal == pdone && !text_new) - { - // Complete state, empty message: close dialog - break; - } if (show_overlay_message) { @@ -241,6 +249,13 @@ void progress_dialog_server::operator()() rsx::overlays::refresh_message_queue(); } + // Leave only if total count is equal to done count + if (ftotal == fdone && ptotal == pdone && !text_new) + { + // Complete state, empty message: close dialog + break; + } + thread_ctrl::wait_for(10000); } From b900c43cebd294fa54ea486c11be9489f0776d35 Mon Sep 17 00:00:00 2001 From: Eladash Date: Sat, 2 Sep 2023 20:07:35 +0300 Subject: [PATCH 180/184] PPU: Precompile only encrypted executeables Improve sys_prx_load_module and sys_overlay_load_module error checking. --- rpcs3/Crypto/unself.cpp | 15 +++++++++++++-- rpcs3/Crypto/unself.h | 2 +- rpcs3/Emu/Cell/PPUThread.cpp | 2 +- rpcs3/Emu/Cell/lv2/sys_overlay.cpp | 2 +- rpcs3/Emu/Cell/lv2/sys_prx.cpp | 4 ++-- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/rpcs3/Crypto/unself.cpp b/rpcs3/Crypto/unself.cpp index d693ae5abb..a553bca1bd 100644 --- a/rpcs3/Crypto/unself.cpp +++ b/rpcs3/Crypto/unself.cpp @@ -1403,7 +1403,7 @@ static bool CheckDebugSelf(fs::file& s) return false; } -fs::file decrypt_self(fs::file elf_or_self, u8* klic_key, SelfAdditionalInfo* out_info) +fs::file decrypt_self(fs::file elf_or_self, u8* klic_key, SelfAdditionalInfo* out_info, bool require_encrypted) { if (out_info) { @@ -1418,8 +1418,14 @@ fs::file decrypt_self(fs::file elf_or_self, u8* klic_key, SelfAdditionalInfo* ou elf_or_self.seek(0); // Check SELF header first. Check for a debug SELF. - if (elf_or_self.size() >= 4 && elf_or_self.read() == "SCE\0"_u32 && !CheckDebugSelf(elf_or_self)) + if (elf_or_self.size() >= 4 && elf_or_self.read() == "SCE\0"_u32) { + if (CheckDebugSelf(elf_or_self)) + { + // TODO: Decrypt + return elf_or_self; + } + // Check the ELF file class (32 or 64 bit). const bool isElf32 = IsSelfElf32(elf_or_self); @@ -1451,6 +1457,11 @@ fs::file decrypt_self(fs::file elf_or_self, u8* klic_key, SelfAdditionalInfo* ou return self_dec.MakeElf(isElf32); } + if (require_encrypted) + { + return {}; + } + return elf_or_self; } diff --git a/rpcs3/Crypto/unself.h b/rpcs3/Crypto/unself.h index 4cc5d90ee3..afa3416095 100644 --- a/rpcs3/Crypto/unself.h +++ b/rpcs3/Crypto/unself.h @@ -559,7 +559,7 @@ private: } }; -fs::file decrypt_self(fs::file elf_or_self, u8* klic_key = nullptr, SelfAdditionalInfo* additional_info = nullptr); +fs::file decrypt_self(fs::file elf_or_self, u8* klic_key = nullptr, SelfAdditionalInfo* additional_info = nullptr, bool require_encrypted = false); bool verify_npdrm_self_headers(const fs::file& self, u8* klic_key = nullptr, NPD_HEADER* npd_out = nullptr); bool get_npdrm_self_header(const fs::file& self, NPD_HEADER& npd); diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index d9144a2d5b..b6f98df46f 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -3890,7 +3890,7 @@ extern void ppu_precompile(std::vector& dir_queue, std::vector ovlmid, const std::string& vp u128 klic = g_fxo->get().last_key(); - ppu_exec_object obj = decrypt_self(std::move(src), reinterpret_cast(&klic)); + ppu_exec_object obj = decrypt_self(std::move(src), reinterpret_cast(&klic), nullptr, true); if (obj != elf_error::ok) { diff --git a/rpcs3/Emu/Cell/lv2/sys_prx.cpp b/rpcs3/Emu/Cell/lv2/sys_prx.cpp index 4cde5d52e9..4a9b7d4ee5 100644 --- a/rpcs3/Emu/Cell/lv2/sys_prx.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_prx.cpp @@ -263,11 +263,11 @@ static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptrget().last_key(); - ppu_prx_object obj = decrypt_self(std::move(src), reinterpret_cast(&klic)); + ppu_prx_object obj = decrypt_self(std::move(src), reinterpret_cast(&klic), nullptr, true); if (obj != elf_error::ok) { - return CELL_PRX_ERROR_ILLEGAL_LIBRARY; + return CELL_PRX_ERROR_UNSUPPORTED_PRX_TYPE; } const auto prx = ppu_load_prx(obj, false, path, file_offset); From 8d9e9eaff96a0bb4bd90da3fdfa3cb0e75913992 Mon Sep 17 00:00:00 2001 From: Eladash Date: Sat, 2 Sep 2023 20:55:36 +0300 Subject: [PATCH 181/184] PPU Precompilation: Fixup file counter --- rpcs3/Emu/Cell/PPUThread.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index b6f98df46f..deba0ece06 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -3749,7 +3749,7 @@ extern void ppu_precompile(std::vector& dir_queue, std::vector& dir_queue, std::vector& dir_queue, std::vector& dir_queue, std::vectorget() = std::move(main_module); From 1c8f1b2c27d578acee3020d2caba542dc1dc50df Mon Sep 17 00:00:00 2001 From: Eladash Date: Sat, 2 Sep 2023 21:05:20 +0300 Subject: [PATCH 182/184] SPU LLVM: Sad workaround for precompilation Disable progress dialog for when only precompilation is required. --- rpcs3/Emu/Cell/SPURecompiler.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index 2f7bcfbf0f..578d68bcf5 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -764,8 +764,9 @@ void spu_cache::initialize(bool build_existing_cache) // Initialize progress dialog (wait for previous progress done) while (u32 v = g_progr_ptotal) { - if (Emu.IsStopped()) + if (Emu.IsStopped() || !build_existing_cache) { + // Workaround: disable progress dialog updates in the case of sole SPU precompilation break; } @@ -776,7 +777,7 @@ void spu_cache::initialize(bool build_existing_cache) if (add_count) { - g_progr_ptotal += add_count; + g_progr_ptotal += build_existing_cache ? add_count : 0; progr.emplace("Building SPU cache..."); } @@ -812,7 +813,7 @@ void spu_cache::initialize(bool build_existing_cache) std::vector> ls(0x10000); // Build functions - for (usz func_i = fnext++; func_i < func_list.size(); func_i = fnext++, g_progr_pdone++) + for (usz func_i = fnext++; func_i < func_list.size(); func_i = fnext++, g_progr_pdone += build_existing_cache ? 1 : 0) { const spu_program& func = std::as_const(func_list)[func_i]; @@ -875,7 +876,7 @@ void spu_cache::initialize(bool build_existing_cache) u32 last_sec_idx = umax; - for (usz func_i = data_indexer++;; func_i = data_indexer++, g_progr_pdone++) + for (usz func_i = data_indexer++;; func_i = data_indexer++, g_progr_pdone += build_existing_cache ? 1 : 0) { u32 passed_count = 0; u32 func_addr = 0; From b5dac0c3337ef62803a58f8f51dce9acdf2f31f3 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sun, 3 Sep 2023 10:02:36 +0200 Subject: [PATCH 183/184] Progress dialog: show analysing... while no files or modules are known yet. --- rpcs3/Emu/system_progress.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/rpcs3/Emu/system_progress.cpp b/rpcs3/Emu/system_progress.cpp index b2be9e84e8..6d5287d163 100644 --- a/rpcs3/Emu/system_progress.cpp +++ b/rpcs3/Emu/system_progress.cpp @@ -212,10 +212,17 @@ void progress_dialog_server::operator()() std::string progr = "Progress:"; - if (ftotal) - fmt::append(progr, " file %u of %u%s", fdone, ftotal, ptotal ? "," : ""); - if (ptotal) - fmt::append(progr, " module %u of %u", pdone, ptotal); + if (ftotal || ptotal) + { + if (ftotal) + fmt::append(progr, " file %u of %u%s", fdone, ftotal, ptotal ? "," : ""); + if (ptotal) + fmt::append(progr, " module %u of %u", pdone, ptotal); + } + else + { + fmt::append(progr, " analysing..."); + } // Changes detected, send update if (native_dlg) From f16d4f05238a0f3a05d6171cb0e284ec4433c4ac Mon Sep 17 00:00:00 2001 From: nastys <7950891+nastys@users.noreply.github.com> Date: Sun, 3 Sep 2023 14:22:28 +0200 Subject: [PATCH 184/184] Update build-mac.sh --- .ci/build-mac.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/build-mac.sh b/.ci/build-mac.sh index e5fce3877f..f3c48781b7 100755 --- a/.ci/build-mac.sh +++ b/.ci/build-mac.sh @@ -6,7 +6,7 @@ brew install -f --overwrite nasm ninja git p7zip create-dmg ccache pipenv #/usr/sbin/softwareupdate --install-rosetta --agree-to-license arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" arch -x86_64 /usr/local/bin/brew update -arch -x86_64 /usr/local/bin/brew install --build-from-source ffmpeg gnutls +arch -x86_64 /usr/local/bin/brew install --build-from-source ffmpeg gnutls freetype arch -x86_64 /usr/local/bin/brew install -f --overwrite llvm@16 glew cmake sdl2 vulkan-headers coreutils arch -x86_64 /usr/local/bin/brew link -f llvm@16