diff --git a/rpcs3/Emu/Cell/Modules/cellCamera.cpp b/rpcs3/Emu/Cell/Modules/cellCamera.cpp index 6fd923ad5174..096f9330f44f 100644 --- a/rpcs3/Emu/Cell/Modules/cellCamera.cpp +++ b/rpcs3/Emu/Cell/Modules/cellCamera.cpp @@ -1139,8 +1139,10 @@ error_code cellCameraGetBufferInfo(s32 dev_num, vm::ptr info) return CELL_OK; } -error_code cellCameraGetBufferInfoEx(s32 dev_num, vm::ptr info) +error_code cellCameraGetBufferInfoEx(ppu_thread& ppu, s32 dev_num, vm::ptr info) { + ppu.state += cpu_flag::wait; + cellCamera.notice("cellCameraGetBufferInfoEx(dev_num=%d, info=0x%x)", dev_num, info); // calls cellCameraGetBufferInfo @@ -1151,10 +1153,16 @@ error_code cellCameraGetBufferInfoEx(s32 dev_num, vm::ptr info } auto& g_camera = g_fxo->get(); - std::lock_guard lock(g_camera.mutex); - *info = g_camera.info; + CellCameraInfoEx info_out; + + { + std::lock_guard lock(g_camera.mutex); + + info_out = g_camera.info; + } + *info = info_out; return CELL_OK; } diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp index f7fbf90fae74..7f02a0eabefe 100644 --- a/rpcs3/Emu/Cell/Modules/cellGem.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp @@ -2308,8 +2308,10 @@ error_code cellGemConvertVideoFinish(ppu_thread& ppu) return CELL_OK; } -error_code cellGemConvertVideoStart(vm::cptr video_frame) +error_code cellGemConvertVideoStart(ppu_thread& ppu, vm::cptr video_frame) { + ppu.state += cpu_flag::wait; + cellGem.warning("cellGemConvertVideoStart(video_frame=*0x%x)", video_frame); auto& gem = g_fxo->get(); @@ -2461,6 +2463,8 @@ error_code cellGemEnableMagnetometer2(u32 gem_num, u32 enable) error_code cellGemEnd(ppu_thread& ppu) { + ppu.state += cpu_flag::wait; + cellGem.warning("cellGemEnd()"); auto& gem = g_fxo->get(); @@ -3265,7 +3269,7 @@ error_code cellGemPrepareCamera(s32 max_exposure, f32 image_quality) extern error_code cellCameraGetAttribute(s32 dev_num, s32 attrib, vm::ptr arg1, vm::ptr arg2); extern error_code cellCameraSetAttribute(s32 dev_num, s32 attrib, u32 arg1, u32 arg2); - extern error_code cellCameraGetBufferInfoEx(s32 dev_num, vm::ptr info); + extern error_code cellCameraGetBufferInfoEx(ppu_thread&, s32 dev_num, vm::ptr info); vm::var info = vm::make_var({}); vm::var arg1 = vm::make_var({}); @@ -3273,7 +3277,7 @@ error_code cellGemPrepareCamera(s32 max_exposure, f32 image_quality) cellCameraGetAttribute(0, 0x3e6, arg1, arg2); cellCameraSetAttribute(0, 0x3e6, 0x3e, *arg2 | 0x80); - cellCameraGetBufferInfoEx(0, info); + cellCameraGetBufferInfoEx(*cpu_thread::get_current(), 0, info); if (info->width == 640) { @@ -3605,6 +3609,8 @@ error_code cellGemTrackHues(vm::cptr req_hues, vm::ptr res_hues) error_code cellGemUpdateFinish(ppu_thread& ppu) { + ppu.state += cpu_flag::wait; + cellGem.warning("cellGemUpdateFinish()"); auto& gem = g_fxo->get(); diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 21c1eade16e0..2673685bb247 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -4125,7 +4125,7 @@ bool spu_thread::process_mfc_cmd() if (raddr != addr) { // Last check for event before we replace the reservation with a new one - if (reservation_check(raddr, rdata)) + if (~ch_events.load().events & SPU_EVENT_LR && reservation_check(raddr, rdata, addr)) { set_events(SPU_EVENT_LR); } @@ -4821,7 +4821,7 @@ bool spu_thread::process_mfc_cmd() ch_mfc_cmd.cmd, ch_mfc_cmd.lsa, ch_mfc_cmd.eal, ch_mfc_cmd.tag, ch_mfc_cmd.size); } -bool spu_thread::reservation_check(u32 addr, const decltype(rdata)& data) const +bool spu_thread::reservation_check(u32 addr, const decltype(rdata)& data, u32 current_eal) const { if (!addr) { @@ -4840,9 +4840,24 @@ bool spu_thread::reservation_check(u32 addr, const decltype(rdata)& data) const return !cmp_rdata(data, *vm::get_super_ptr(addr)); } + if ((addr >> 20) == (current_eal >> 20)) + { + if (vm::check_addr(addr, vm::page_1m_size)) + { + // Same random-access-memory page as the current MFC command, assume allocated + return !cmp_rdata(data, vm::_ref(addr)); + } + + if ((addr >> 16) == (current_eal >> 16) && vm::check_addr(addr, vm::page_64k_size)) + { + // Same random-access-memory page as the current MFC command, assume allocated + return !cmp_rdata(data, vm::_ref(addr)); + } + } + // Ensure data is allocated (HACK: would raise LR event if not) // Set range_lock first optimistically - range_lock->store(u64{128} << 32 | addr); + range_lock->store(u64{128} << 32 | addr | vm::range_readable); u64 lock_val = *std::prev(std::end(vm::g_range_lock_set)); u64 old_lock = 0; @@ -4923,7 +4938,7 @@ bool spu_thread::reservation_check(u32 addr, u32 hash, atomic_t* range_ // Ensure data is allocated (HACK: would raise LR event if not) // Set range_lock first optimistically - range_lock->store(u64{128} << 32 | addr); + range_lock->store(u64{128} << 32 | addr | vm::range_readable); u64 lock_val = *std::prev(std::end(vm::g_range_lock_set)); u64 old_lock = 0; diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 36d24ff55e9a..9adb15a47cfd 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -901,7 +901,8 @@ class spu_thread : public cpu_thread // Returns true if reservation existed but was just discovered to be lost // It is safe to use on any address, even if not directly accessed by SPU (so it's slower) - bool reservation_check(u32 addr, const decltype(rdata)& data) const; + // Optionally pass a known allocated address for internal optimization (the current Effective-Address of the MFC command) + bool reservation_check(u32 addr, const decltype(rdata)& data, u32 current_eal = 0) const; static bool reservation_check(u32 addr, u32 hash, atomic_t* range_lock); usz register_cache_line_waiter(u32 addr); void deregister_cache_line_waiter(usz index); diff --git a/rpcs3/Emu/Cell/lv2/sys_memory.cpp b/rpcs3/Emu/Cell/lv2/sys_memory.cpp index fb8c5b51ace8..6110d7d4d59f 100644 --- a/rpcs3/Emu/Cell/lv2/sys_memory.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_memory.cpp @@ -5,6 +5,7 @@ #include "Emu/CPU/CPUThread.h" #include "Emu/Cell/ErrorCodes.h" #include "Emu/Cell/SPUThread.h" +#include "Emu/Cell/PPUThread.h" #include "Emu/IdManager.h" #include "util/asm.hpp" @@ -249,17 +250,37 @@ error_code sys_memory_free(cpu_thread& cpu, u32 addr) return CELL_OK; } -error_code sys_memory_get_page_attribute(cpu_thread& cpu, u32 addr, vm::ptr attr) +error_code sys_memory_get_page_attribute(ppu_thread& ppu, u32 addr, vm::ptr attr) { - cpu.state += cpu_flag::wait; + ppu.state += cpu_flag::wait; sys_memory.trace("sys_memory_get_page_attribute(addr=0x%x, attr=*0x%x)", addr, attr); - vm::writer_lock rlock; + if ((addr >> 28) == (ppu.stack_addr >> 28)) + { + // Stack address: fast path + if (!(addr >= ppu.stack_addr && addr < ppu.stack_addr + ppu.stack_size) && !vm::check_addr(addr)) + { + return { CELL_EINVAL, addr }; + } + + if (!vm::check_addr(attr.addr(), vm::page_readable, attr.size())) + { + return CELL_EFAULT; + } - if (!vm::check_addr(addr) || addr >= SPU_FAKE_BASE_ADDR) + attr->attribute = 0x40000ull; // SYS_MEMORY_PROT_READ_WRITE + attr->access_right = SYS_MEMORY_ACCESS_RIGHT_PPU_THR; + attr->page_size = 4096; + attr->pad = 0; // Always write 0 + return CELL_OK; + } + + const auto [ok, vm_flags] = vm::get_addr_flags(addr); + + if (!ok || addr >= SPU_FAKE_BASE_ADDR) { - return CELL_EINVAL; + return { CELL_EINVAL, addr }; } if (!vm::check_addr(attr.addr(), vm::page_readable, attr.size())) @@ -268,19 +289,20 @@ error_code sys_memory_get_page_attribute(cpu_thread& cpu, u32 addr, vm::ptrattribute = 0x40000ull; // SYS_MEMORY_PROT_READ_WRITE (TODO) - attr->access_right = addr >> 28 == 0xdu ? SYS_MEMORY_ACCESS_RIGHT_PPU_THR : SYS_MEMORY_ACCESS_RIGHT_ANY;// (TODO) + attr->access_right = SYS_MEMORY_ACCESS_RIGHT_ANY; // TODO: Report accurately - if (vm::check_addr(addr, vm::page_1m_size)) + if (vm_flags & vm::page_1m_size) { attr->page_size = 0x100000; } - else if (vm::check_addr(addr, vm::page_64k_size)) + else if (vm_flags & vm::page_64k_size) { attr->page_size = 0x10000; } else { - attr->page_size = 4096; + //attr->page_size = 4096; + fmt::throw_exception("Unreachable"); } attr->pad = 0; // Always write 0 diff --git a/rpcs3/Emu/Cell/lv2/sys_memory.h b/rpcs3/Emu/Cell/lv2/sys_memory.h index c2ca046bccf0..2702c45ac290 100644 --- a/rpcs3/Emu/Cell/lv2/sys_memory.h +++ b/rpcs3/Emu/Cell/lv2/sys_memory.h @@ -4,6 +4,7 @@ #include "Emu/Cell/ErrorCodes.h" class cpu_thread; +class ppu_thread; enum lv2_mem_container_id : u32 { @@ -131,7 +132,7 @@ struct sys_memory_user_memory_stat_t error_code sys_memory_allocate(cpu_thread& cpu, u64 size, u64 flags, vm::ptr alloc_addr); error_code sys_memory_allocate_from_container(cpu_thread& cpu, u64 size, u32 cid, u64 flags, vm::ptr alloc_addr); error_code sys_memory_free(cpu_thread& cpu, u32 start_addr); -error_code sys_memory_get_page_attribute(cpu_thread& cpu, u32 addr, vm::ptr attr); +error_code sys_memory_get_page_attribute(ppu_thread& cpu, u32 addr, vm::ptr attr); error_code sys_memory_get_user_memory_size(cpu_thread& cpu, vm::ptr mem_info); error_code sys_memory_get_user_memory_stat(cpu_thread& cpu, vm::ptr mem_stat); error_code sys_memory_container_create(cpu_thread& cpu, vm::ptr cid, u64 size); diff --git a/rpcs3/Emu/Memory/vm.cpp b/rpcs3/Emu/Memory/vm.cpp index b118ac80274c..6181c2c6bbfd 100644 --- a/rpcs3/Emu/Memory/vm.cpp +++ b/rpcs3/Emu/Memory/vm.cpp @@ -547,6 +547,13 @@ namespace vm { to_clear = for_all_range_locks(to_clear & ~get_range_lock_bits(true), [&](u64 addr2, u32 size2) { + constexpr u32 range_size_loc = vm::range_pos - 32; + + if ((size2 >> range_size_loc) == (vm::range_readable >> vm::range_pos)) + { + return 0; + } + // Split and check every 64K page separately for (u64 hi = addr2 >> 16, max = (addr2 + size2 - 1) >> 16; hi <= max; hi++) { diff --git a/rpcs3/Emu/Memory/vm.h b/rpcs3/Emu/Memory/vm.h index 3c39d0aa4a01..09d2d1542177 100644 --- a/rpcs3/Emu/Memory/vm.h +++ b/rpcs3/Emu/Memory/vm.h @@ -81,7 +81,7 @@ namespace vm bool check_addr(u32 addr, u8 flags, u32 size); template - bool check_addr(u32 addr, u8 flags = page_readable) + inline bool check_addr(u32 addr, u8 flags = page_readable) { extern std::array g_pages; @@ -94,6 +94,16 @@ namespace vm return !(~g_pages[addr / 4096] & (flags | page_allocated)); } + // Like check_addr but should only be used in lock-free context with care + inline std::pair get_addr_flags(u32 addr) noexcept + { + extern std::array g_pages; + + const u8 flags = g_pages[addr / 4096].load(); + + return std::make_pair(!!(flags & page_allocated), flags); + } + // Read string in a safe manner (page aware) (bool true = if null-termination) bool read_string(u32 addr, u32 max_size, std::string& out_string, bool check_pages = true) noexcept; diff --git a/rpcs3/rpcs3qt/kernel_explorer.cpp b/rpcs3/rpcs3qt/kernel_explorer.cpp index 360c35210017..68c6f45e2b5e 100644 --- a/rpcs3/rpcs3qt/kernel_explorer.cpp +++ b/rpcs3/rpcs3qt/kernel_explorer.cpp @@ -651,15 +651,29 @@ void kernel_explorer::update() return fmt::format(" (%.1fs)", wait_time); }; + std::vector> ppu_threads; + idm::select>([&](u32 id, ppu_thread& ppu) { const auto func = ppu.last_function; const ppu_thread_status status = lv2_obj::ppu_state(&ppu, false, false).first; - add_leaf(find_node(root, additional_nodes::ppu_threads), QString::fromStdString(fmt::format(u8"PPU 0x%07x: “%s”, PRIO: %d, Joiner: %s, Status: %s, State: %s, %s func: “%s”%s", id, *ppu.ppu_tname.load(), ppu.prio.load().prio, ppu.joiner.load(), status, ppu.state.load() - , ppu.ack_suspend ? "After" : (ppu.current_function ? "In" : "Last"), func ? func : "", get_wait_time_str(ppu.start_time)))); + const s32 prio = ppu.prio.load().prio; + std::string prio_text = fmt::format("%4d", prio); + prio_text = fmt::replace_all(prio_text, " ", " "); + + ppu_threads.emplace_back(prio, fmt::format(u8"PPU 0x%07x: PRIO: %s, “%s”Joiner: %s, Status: %s, State: %s, %s func: “%s”%s", id, prio_text, *ppu.ppu_tname.load(), ppu.joiner.load(), status, ppu.state.load() + , ppu.ack_suspend ? "After" : (ppu.current_function ? "In" : "Last"), func ? func : "", get_wait_time_str(ppu.start_time))); }, idm::unlocked); + // Sort by priority + std::stable_sort(ppu_threads.begin(), ppu_threads.end(), FN(x.first < y.first)); + + for (const auto& [prio, text] : ppu_threads) + { + add_leaf(find_node(root, additional_nodes::ppu_threads), QString::fromStdString(text)); + } + lock_idm_lv2.reset(); idm::select>([&](u32 /*id*/, spu_thread& spu)