diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 5f2c2ab6c0..435aa7ddbf 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -48,7 +48,6 @@ #include "define_new_memleakdetect.h" #include "PPUTranslator.h" -#include "Modules/cellMsgDialog.h" #endif #include @@ -67,7 +66,9 @@ const bool s_use_ssse3 = extern u64 get_system_time(); - +extern atomic_t g_progr; +extern atomic_t g_progr_ptotal; +extern atomic_t g_progr_pdone; enum class join_status : u32 { @@ -164,7 +165,7 @@ extern const ppu_decoder g_ppu_interpreter_fast([](auto& t extern void ppu_initialize(); extern void ppu_initialize(const ppu_module& info); -static void ppu_initialize2(class jit_compiler& jit, const ppu_module& module_part, const std::string& cache_path, const std::string& obj_name, u32 fragment_index, const std::shared_ptr>&); +static void ppu_initialize2(class jit_compiler& jit, const ppu_module& module_part, const std::string& cache_path, const std::string& obj_name); extern void ppu_execute_syscall(ppu_thread& ppu, u64 code); // Get pointer to executable cache @@ -1337,6 +1338,9 @@ extern void ppu_initialize(const ppu_module& info) } #ifdef LLVM_AVAILABLE + // Initialize progress dialog + g_progr = "Compiling PPU modules..."; + // Compiled PPU module info struct jit_module { @@ -1380,10 +1384,6 @@ extern void ppu_initialize(const ppu_module& info) // Difference between function name and current location const u32 reloc = info.name.empty() ? 0 : info.segs.at(0).addr; - std::shared_ptr> fragment_sync = std::make_shared>(0); - - u32 fragment_count{0}; - while (jit_mod.vars.empty() && fpos < info.funcs.size()) { // Initialize compiler instance @@ -1546,8 +1546,11 @@ extern void ppu_initialize(const ppu_module& info) continue; } + // Update progress dialog + g_progr_ptotal++; + // Create worker thread for compilation - jthreads.emplace_back([&jit, obj_name = obj_name, part = std::move(part), &cache_path, fragment_sync, jcores, findex = ::size32(jthreads)]() + jthreads.emplace_back([&jit, obj_name = obj_name, part = std::move(part), &cache_path, jcores]() { // Set low priority thread_ctrl::set_native_priority(-1); @@ -1556,14 +1559,14 @@ extern void ppu_initialize(const ppu_module& info) { semaphore_lock jlock(jcores->sem); - if (Emu.IsStopped()) + if (!Emu.IsStopped()) { - return; + // Use another JIT instance + jit_compiler jit2({}, g_cfg.core.llvm_cpu); + ppu_initialize2(jit2, part, cache_path, obj_name); } - // Use another JIT instance - jit_compiler jit2({}, g_cfg.core.llvm_cpu); - ppu_initialize2(jit2, part, cache_path, obj_name, findex, fragment_sync); + g_progr_pdone++; } if (Emu.IsStopped() || !jit || !fs::is_file(cache_path + obj_name)) @@ -1577,9 +1580,6 @@ extern void ppu_initialize(const ppu_module& info) }); } - // Initialize fragment count sync var - fragment_sync->exchange(::size32(jthreads)); - // Join worker threads for (auto& thread : jthreads) { @@ -1663,7 +1663,7 @@ extern void ppu_initialize(const ppu_module& info) #endif } -static void ppu_initialize2(jit_compiler& jit, const ppu_module& module_part, const std::string& cache_path, const std::string& obj_name, u32 fragment_index, const std::shared_ptr>& fragment_sync) +static void ppu_initialize2(jit_compiler& jit, const ppu_module& module_part, const std::string& cache_path, const std::string& obj_name) { #ifdef LLVM_AVAILABLE using namespace llvm; @@ -1691,8 +1691,6 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module& module_part, co } } - std::shared_ptr dlg; - { legacy::FunctionPassManager pm(module.get()); @@ -1716,25 +1714,6 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module& module_part, co //pm.add(createCFGSimplificationPass()); //pm.add(createLintPass()); // Check - // Initialize message dialog - dlg = Emu.GetCallbacks().get_msg_dialog(); - dlg->type.se_normal = true; - dlg->type.bg_invisible = true; - dlg->type.progress_bar_count = 1; - dlg->on_close = [](s32 status) - { - Emu.CallAfter([]() - { - // Abort everything - Emu.Stop(); - }); - }; - - Emu.CallAfter([=]() - { - dlg->Create("Compiling PPU module:\n" + obj_name + "\nPlease wait..."); - }); - // Translate functions for (size_t fi = 0, fmax = module_part.funcs.size(); fi < fmax; fi++) { @@ -1746,18 +1725,6 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module& module_part, co if (module_part.funcs[fi].size) { - // Update dialog - Emu.CallAfter([=, max = module_part.funcs.size()]() - { - dlg->ProgressBarSetMsg(0, fmt::format("Compiling %u of %u", fi + 1, fmax)); - - if (fi * 100 / fmax != (fi + 1) * 100 / fmax) - dlg->ProgressBarInc(0, 1); - - if (u32 fragment_count = fragment_sync->load()) - dlg->SetMsg(fmt::format("Compiling PPU module (%u of %u):\n%s\nPlease wait...", fragment_index + 1, fragment_count, obj_name)); - }); - // Translate if (const auto func = translator.Translate(module_part.funcs[fi])) { @@ -1780,16 +1747,6 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module& module_part, co //mpm.add(createDeadInstEliminationPass()); //mpm.run(*module); - // Update dialog - Emu.CallAfter([=]() - { - dlg->ProgressBarSetMsg(0, "Generating code, this may take a long time..."); - dlg->ProgressBarInc(0, 100); - - if (u32 fragment_count = fragment_sync->load()) - dlg->SetMsg(fmt::format("Compiling PPU module (%u of %u):\n%s\nPlease wait...", fragment_index + 1, fragment_count, obj_name)); - }); - std::string result; raw_string_ostream out(result); diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 4197dfb654..d6a8a7ecd0 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -63,6 +63,13 @@ extern void network_thread_init(); fs::file g_tty; atomic_t g_tty_size{0}; +// Progress display server synchronization variables +atomic_t g_progr{nullptr}; +atomic_t g_progr_ftotal{0}; +atomic_t g_progr_fdone{0}; +atomic_t g_progr_ptotal{0}; +atomic_t g_progr_pdone{0}; + template <> void fmt_class_string::format(std::string& out, u64 arg) { @@ -260,6 +267,100 @@ void Emulator::Init() #endif // Initialize patch engine fxm::make_always()->append(fs::get_config_dir() + "/patch.yml"); + + // Initialize progress dialog server (TODO) + if (g_progr.exchange("") == nullptr) + { + std::thread server([]() + { + while (true) + { + std::shared_ptr dlg; + + // Wait for the start condition + while (!g_progr_ftotal && !g_progr_ptotal) + { + std::this_thread::sleep_for(5ms); + } + + // Initialize message dialog + dlg = Emu.GetCallbacks().get_msg_dialog(); + dlg->type.se_normal = true; + dlg->type.bg_invisible = true; + dlg->type.progress_bar_count = 1; + dlg->on_close = [](s32 status) + { + Emu.CallAfter([]() + { + // Abort everything + Emu.Stop(); + }); + }; + + Emu.CallAfter([=]() + { + dlg->Create(+g_progr); + }); + + u64 ftotal = 0; + u64 fdone = 0; + u64 ptotal = 0; + u64 pdone = 0; + u32 value = 0; + + // Update progress + while (true) + { + if (ftotal != g_progr_ftotal || fdone != g_progr_fdone || ptotal != g_progr_ptotal || pdone != g_progr_pdone) + { + ftotal = g_progr_ftotal; + fdone = g_progr_fdone; + ptotal = g_progr_ptotal; + pdone = g_progr_pdone; + + // Compute new progress in percents + const u32 new_value = ((ptotal ? pdone * 1. / ptotal : 0.) + fdone) * 100. / (ftotal ? ftotal : 1); + + // Compute the difference + const u32 delta = new_value > value ? new_value - value : 0; + + value += delta; + + // Changes detected, send update + Emu.CallAfter([=]() + { + 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); + + dlg->SetMsg(+g_progr); + dlg->ProgressBarSetMsg(0, progr); + dlg->ProgressBarInc(0, delta); + }); + } + + if (fdone >= ftotal && pdone >= ptotal) + { + // Close dialog + break; + } + + std::this_thread::sleep_for(10ms); + } + + // Cleanup + g_progr_ftotal -= fdone; + g_progr_fdone -= fdone; + g_progr_ptotal -= pdone; + g_progr_pdone -= pdone; + } + }); + + server.detach(); + } } bool Emulator::BootRsxCapture(const std::string& path) @@ -565,9 +666,15 @@ void Emulator::Load(bool add_only) std::vector dir_queue; dir_queue.emplace_back(m_path + '/'); + std::vector> file_queue; + file_queue.reserve(2000); + std::queue> thread_queue; const uint max_threads = std::thread::hardware_concurrency(); + // Initialize progress dialog + g_progr = "Scanning directories for SPRX libraries..."; + // Find all .sprx files recursively (TODO: process .mself files) for (std::size_t i = 0; i < dir_queue.size(); i++) { @@ -604,39 +711,55 @@ void Emulator::Load(bool add_only) } // Get full path - const std::string path = dir_queue[i] + entry.name; - - LOG_NOTICE(LOADER, "Trying to load SPRX: %s", path); - - // Some files may fail to decrypt due to the lack of klic - const ppu_prx_object obj = decrypt_self(fs::file(path)); - - if (obj == elf_error::ok) - { - if (auto prx = ppu_load_prx(obj, path)) - { - while (g_thread_count >= max_threads + 2) - { - std::this_thread::sleep_for(10ms); - } - - thread_queue.emplace(); - - thread_ctrl::spawn(thread_queue.back(), "Worker " + std::to_string(thread_queue.size()), [_prx = std::move(prx)] - { - ppu_initialize(*_prx); - ppu_unload_prx(*_prx); - }); - } - } - else - { - LOG_ERROR(LOADER, "Failed to load SPRX '%s' (%s)", path, obj.get_error()); - } + file_queue.emplace_back(dir_queue[i] + entry.name, 0); + g_progr_ftotal++; } } } + for (std::size_t i = 0; i < file_queue.size(); i++) + { + const auto& path = file_queue[i].first; + + LOG_NOTICE(LOADER, "Trying to load SPRX: %s", path); + + // Load MSELF or SPRX + fs::file src{path}; + + if (file_queue[i].second == 0) + { + // Some files may fail to decrypt due to the lack of klic + src = decrypt_self(std::move(src)); + } + + const ppu_prx_object obj = src; + + if (obj == elf_error::ok) + { + if (auto prx = ppu_load_prx(obj, path)) + { + while (g_thread_count >= max_threads + 2) + { + std::this_thread::sleep_for(10ms); + } + + thread_queue.emplace(); + + thread_ctrl::spawn(thread_queue.back(), "Worker " + std::to_string(thread_queue.size()), [_prx = std::move(prx)] + { + ppu_initialize(*_prx); + ppu_unload_prx(*_prx); + g_progr_fdone++; + }); + + continue; + } + } + + LOG_ERROR(LOADER, "Failed to load SPRX '%s' (%s)", path, obj.get_error()); + g_progr_fdone++; + } + // Join every thread and print exceptions while (!thread_queue.empty()) {