diff --git a/BUILDING.md b/BUILDING.md index 263fed68ed..9133596547 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -19,7 +19,7 @@ The following tools are required to build RPCS3 on Windows 10 or later: with standalone **CMake** tool. - [Python 3.6+](https://www.python.org/downloads/) (add to PATH) -- [Qt 6.7.3](https://www.qt.io/download-qt-installer) +- [Qt 6.7.3](https://www.qt.io/download-qt-installer) In case you can't download from the official installer, you can use [Another Qt installer](https://github.com/miurahr/aqtinstall) (In that case you will need to manually add the "qtmultimedia" module when installing Qt) - [Vulkan SDK 1.3.268.0](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.268.0. The `sln` solution available only on **Visual Studio** is the preferred building solution. It easily allows to build the **RPCS3** application in `Release` and `Debug` mode. diff --git a/Utilities/File.cpp b/Utilities/File.cpp index 85e6c1cddf..89d53f9407 100644 --- a/Utilities/File.cpp +++ b/Utilities/File.cpp @@ -2475,7 +2475,6 @@ bool fs::pending_file::commit(bool overwrite) { file.sync(); } - #endif #ifdef _WIN32 @@ -2486,15 +2485,98 @@ bool fs::pending_file::commit(bool overwrite) disp.DeleteFileW = false; ensure(SetFileInformationByHandle(file.get_handle(), FileDispositionInfo, &disp, sizeof(disp))); } + + std::vector hardlink_paths; + + const auto ws1 = to_wchar(m_path); + + const HANDLE file_handle = !overwrite ? INVALID_HANDLE_VALUE + : CreateFileW(ws1.get(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); + + while (file_handle != INVALID_HANDLE_VALUE) + { + // Get file ID (used to check for hardlinks) + BY_HANDLE_FILE_INFORMATION file_info; + + if (!GetFileInformationByHandle(file_handle, &file_info) || file_info.nNumberOfLinks == 1) + { + CloseHandle(file_handle); + break; + } + + // Buffer for holding link name + std::wstring link_name_buffer(MAX_PATH, wchar_t{}); + DWORD buffer_size{}; + HANDLE find_handle = INVALID_HANDLE_VALUE; + + while (true) + { + buffer_size = static_cast(link_name_buffer.size() - 1); + find_handle = FindFirstFileNameW(ws1.get(), 0, &buffer_size, link_name_buffer.data()); + + if (find_handle != INVALID_HANDLE_VALUE || GetLastError() != ERROR_MORE_DATA) + { + break; + } + + link_name_buffer.resize(buffer_size + 1); + } + + if (find_handle != INVALID_HANDLE_VALUE) + { + const std::wstring_view ws1_sv = ws1.get(); + + while (true) + { + if (link_name_buffer.c_str() != ws1_sv) + { + // Note: link_name_buffer is a buffer which may contain zeroes so truncate it + hardlink_paths.push_back(link_name_buffer.c_str()); + } + + buffer_size = static_cast(link_name_buffer.size() - 1); + if (!FindNextFileNameW(find_handle, &buffer_size, link_name_buffer.data())) + { + if (GetLastError() != ERROR_MORE_DATA) + { + break; + } + + link_name_buffer.resize(buffer_size + 1); + } + } + } + + // Clean up + FindClose(find_handle); + CloseHandle(file_handle); + break; + } + + if (!hardlink_paths.empty()) + { + // REPLACEFILE_WRITE_THROUGH is not supported + file.sync(); + } #endif file.close(); #ifdef _WIN32 - const auto ws1 = to_wchar(m_path); const auto ws2 = to_wchar(m_dest); - if (MoveFileExW(ws1.get(), ws2.get(), overwrite ? MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH : MOVEFILE_WRITE_THROUGH)) + bool ok = false; + + if (hardlink_paths.empty()) + { + ok = MoveFileExW(ws1.get(), ws2.get(), overwrite ? MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH : MOVEFILE_WRITE_THROUGH); + } + else + { + ok = ReplaceFileW(ws1.get(), ws2.get(), nullptr, 0, nullptr, nullptr); + } + + if (ok) { // Disable the destructor m_path.clear(); diff --git a/rpcs3/Emu/Cell/lv2/sys_event.cpp b/rpcs3/Emu/Cell/lv2/sys_event.cpp index 3047f87fa6..1783cdecb1 100644 --- a/rpcs3/Emu/Cell/lv2/sys_event.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_event.cpp @@ -119,8 +119,13 @@ std::shared_ptr lv2_event_queue::find(u64 ipc_key) extern void resume_spu_thread_group_from_waiting(spu_thread& spu); -CellError lv2_event_queue::send(lv2_event event) +CellError lv2_event_queue::send(lv2_event event, bool* notified_thread, lv2_event_port* port) { + if (notified_thread) + { + *notified_thread = false; + } + std::lock_guard lock(mutex); if (!exists) @@ -162,6 +167,15 @@ CellError lv2_event_queue::send(lv2_event event) std::tie(ppu.gpr[4], ppu.gpr[5], ppu.gpr[6], ppu.gpr[7]) = event; awake(&ppu); + + if (port && ppu.prio.load().prio < ensure(cpu_thread::get_current())->prio.load().prio) + { + // Block event port disconnection for the time being of sending events + // PPU -> lower prio PPU is the only case that can cause thread blocking + port->is_busy++; + ensure(notified_thread); + *notified_thread = true; + } } else { @@ -709,7 +723,10 @@ error_code sys_event_port_disconnect(ppu_thread& ppu, u32 eport_id) return CELL_ENOTCONN; } - // TODO: return CELL_EBUSY if necessary (can't detect the condition) + if (port->is_busy) + { + return CELL_EBUSY; + } port->queue.reset(); @@ -718,20 +735,32 @@ error_code sys_event_port_disconnect(ppu_thread& ppu, u32 eport_id) error_code sys_event_port_send(u32 eport_id, u64 data1, u64 data2, u64 data3) { - if (auto cpu = get_current_cpu_thread()) + const auto cpu = cpu_thread::get_current(); + const auto ppu = cpu ? cpu->try_get() : nullptr; + + if (cpu) { cpu->state += cpu_flag::wait; } sys_event.trace("sys_event_port_send(eport_id=0x%x, data1=0x%llx, data2=0x%llx, data3=0x%llx)", eport_id, data1, data2, data3); + bool notified_thread = false; + const auto port = idm::check(eport_id, [&, notify = lv2_obj::notify_all_t()](lv2_event_port& port) -> CellError { + if (ppu && ppu->loaded_from_savestate) + { + port.is_busy++; + notified_thread = true; + return {}; + } + if (lv2_obj::check(port.queue)) { const u64 source = port.name ? port.name : (u64{process_getpid() + 0u} << 32) | u64{eport_id}; - return port.queue->send(source, data1, data2, data3); + return port.queue->send(source, data1, data2, data3, ¬ified_thread, ppu && port.queue->type == SYS_PPU_QUEUE ? &port : nullptr); } return CELL_ENOTCONN; @@ -742,6 +771,19 @@ error_code sys_event_port_send(u32 eport_id, u64 data1, u64 data2, u64 data3) return CELL_ESRCH; } + if (ppu && port->queue->type == SYS_PPU_QUEUE && notified_thread) + { + // Wait to be requeued + if (ppu->test_stopped()) + { + // Wait again on savestate load + ppu->state += cpu_flag::again; + } + + port->is_busy--; + return CELL_OK; + } + if (port.ret) { if (port.ret == CELL_EAGAIN) diff --git a/rpcs3/Emu/Cell/lv2/sys_event.h b/rpcs3/Emu/Cell/lv2/sys_event.h index bf872cc4ba..506a45e7a0 100644 --- a/rpcs3/Emu/Cell/lv2/sys_event.h +++ b/rpcs3/Emu/Cell/lv2/sys_event.h @@ -79,6 +79,8 @@ struct sys_event_t // Source, data1, data2, data3 using lv2_event = std::tuple; +struct lv2_event_port; + struct lv2_event_queue final : public lv2_obj { static const u32 id_base = 0x8d000000; @@ -103,11 +105,11 @@ struct lv2_event_queue final : public lv2_obj static void save_ptr(utils::serial&, lv2_event_queue*); static std::shared_ptr load_ptr(utils::serial& ar, std::shared_ptr& queue, std::string_view msg = {}); - CellError send(lv2_event event); + CellError send(lv2_event event, bool* notified_thread = nullptr, lv2_event_port* port = nullptr); - CellError send(u64 source, u64 d1, u64 d2, u64 d3) + CellError send(u64 source, u64 d1, u64 d2, u64 d3, bool* notified_thread = nullptr, lv2_event_port* port = nullptr) { - return send(std::make_tuple(source, d1, d2, d3)); + return send(std::make_tuple(source, d1, d2, d3), notified_thread, port); } // Get event queue by its global key @@ -121,6 +123,7 @@ struct lv2_event_port final : lv2_obj const s32 type; // Port type, either IPC or local const u64 name; // Event source (generated from id and process id if not set) + atomic_t is_busy = 0; // Counts threads waiting on event sending std::shared_ptr queue; // Event queue this port is connected to lv2_event_port(s32 type, u64 name) diff --git a/rpcs3/Emu/Cell/lv2/sys_event_flag.cpp b/rpcs3/Emu/Cell/lv2/sys_event_flag.cpp index de5e475b06..c5e3ee9781 100644 --- a/rpcs3/Emu/Cell/lv2/sys_event_flag.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_event_flag.cpp @@ -424,6 +424,8 @@ error_code sys_event_flag_set(cpu_thread& cpu, u32 id, u64 bitptn) } } + dependant_mask &= ~bitptn; + auto [new_val, ok] = flag->pattern.fetch_op([&](u64& x) { if ((x ^ pattern) & dependant_mask) diff --git a/rpcs3/Emu/Cell/lv2/sys_time.cpp b/rpcs3/Emu/Cell/lv2/sys_time.cpp index adc15da13a..b04be640bc 100644 --- a/rpcs3/Emu/Cell/lv2/sys_time.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_time.cpp @@ -212,9 +212,9 @@ u64 get_system_time() const u64 tsc = utils::get_tsc(); #if _MSC_VER - const u64 result = static_cast(u128_from_mul(tsc, 1000000ull) / freq) * g_cfg.core.clocks_scale / 100u; + const u64 result = static_cast(u128_from_mul(tsc, 1000000ull) / freq); #else - const u64 result = (tsc / freq * 1000000ull + tsc % freq * 1000000ull / freq) * g_cfg.core.clocks_scale / 100u; + const u64 result = (tsc / freq * 1000000ull + tsc % freq * 1000000ull / freq); #endif return result; } diff --git a/rpcs3/Emu/Memory/vm.cpp b/rpcs3/Emu/Memory/vm.cpp index a0407645f8..82ffc5d47e 100644 --- a/rpcs3/Emu/Memory/vm.cpp +++ b/rpcs3/Emu/Memory/vm.cpp @@ -1009,7 +1009,7 @@ namespace vm // the RSX might try to call VirtualProtect on memory that is already unmapped if (auto rsxthr = g_fxo->try_get()) { - rsxthr->on_notify_memory_unmapped(addr, size); + rsxthr->on_notify_pre_memory_unmapped(addr, size); } // Deregister PPU related data @@ -1309,7 +1309,7 @@ namespace vm } } - bool block_t::unmap() + bool block_t::unmap(std::vector>* unmapped) { auto& m_map = (m.*block_map)(); @@ -1320,7 +1320,13 @@ namespace vm { const auto next = std::next(it); const auto size = it->second.first; - _page_unmap(it->first, size, this->flags, it->second.second.get()); + auto unmap = std::make_pair(it->first, _page_unmap(it->first, size, this->flags, it->second.second.get())); + + if (unmapped) + { + unmapped->emplace_back(unmap); + } + it = next; } @@ -1480,6 +1486,20 @@ namespace vm { auto& m_map = (m.*block_map)(); { + struct notify_t + { + u32 addr{}; + u32 size{}; + + ~notify_t() noexcept + { + if (auto rsxthr = g_fxo->try_get(); rsxthr && size) + { + rsxthr->on_notify_post_memory_unmapped(addr, size); + } + } + } unmap_notification; + vm::writer_lock lock; const auto found = m_map.find(addr - (flags & stack_guarded ? 0x1000 : 0)); @@ -1517,6 +1537,8 @@ namespace vm // Remove entry m_map.erase(found); + unmap_notification.size = size; + unmap_notification.addr = addr; return size; } } @@ -1815,9 +1837,9 @@ namespace vm } } - bool _unmap_block(const std::shared_ptr& block) + bool _unmap_block(const std::shared_ptr& block, std::vector>* unmapped = nullptr) { - return block->unmap(); + return block->unmap(unmapped); } static bool _test_map(u32 addr, u32 size) @@ -1964,6 +1986,22 @@ namespace vm std::pair, bool> result{}; + struct notify_t + { + std::vector> addr_size_pairs; + + ~notify_t() noexcept + { + for (const auto [addr, size] : addr_size_pairs) + { + if (auto rsxthr = g_fxo->try_get()) + { + rsxthr->on_notify_post_memory_unmapped(addr, size); + } + } + } + } unmap_notifications; + vm::writer_lock lock; for (auto it = g_locations.begin() + memory_location_max; it != g_locations.end(); it++) @@ -1993,7 +2031,7 @@ namespace vm result.first = std::move(*it); g_locations.erase(it); - ensure(_unmap_block(result.first)); + ensure(_unmap_block(result.first, &unmap_notifications.addr_size_pairs)); result.second = true; return result; } diff --git a/rpcs3/Emu/Memory/vm.h b/rpcs3/Emu/Memory/vm.h index 05c5d59623..d7f4c66615 100644 --- a/rpcs3/Emu/Memory/vm.h +++ b/rpcs3/Emu/Memory/vm.h @@ -133,8 +133,8 @@ namespace vm bool try_alloc(u32 addr, u64 bflags, u32 size, std::shared_ptr&&) const; // Unmap block - bool unmap(); - friend bool _unmap_block(const std::shared_ptr&); + bool unmap(std::vector>* unmapped = nullptr); + friend bool _unmap_block(const std::shared_ptr&, std::vector>* unmapped); public: block_t(u32 addr, u32 size, u64 flags); diff --git a/rpcs3/Emu/RSX/Common/profiling_timer.hpp b/rpcs3/Emu/RSX/Common/profiling_timer.hpp index 7df016b9d1..88851626cc 100644 --- a/rpcs3/Emu/RSX/Common/profiling_timer.hpp +++ b/rpcs3/Emu/RSX/Common/profiling_timer.hpp @@ -16,7 +16,7 @@ namespace rsx { if (enabled) [[unlikely]] { - last = rsx::uclock(); + last = get_system_time(); } } @@ -28,7 +28,7 @@ namespace rsx } auto old = last; - last = rsx::uclock(); + last = get_system_time(); return static_cast(last - old); } }; diff --git a/rpcs3/Emu/RSX/Common/time.hpp b/rpcs3/Emu/RSX/Common/time.hpp index 430ba3fade..ad3aa56067 100644 --- a/rpcs3/Emu/RSX/Common/time.hpp +++ b/rpcs3/Emu/RSX/Common/time.hpp @@ -4,20 +4,3 @@ #include #include "Emu/Cell/timers.hpp" - -namespace rsx -{ - static inline u64 uclock() - { - static const ullong s_tsc_scaled_freq = (utils::get_tsc_freq() / 1000000); - - if (s_tsc_scaled_freq) - { - return utils::get_tsc() / s_tsc_scaled_freq; - } - else - { - return get_system_time(); - } - } -} diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index 6f7edcd52c..4cf8fb6c38 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -1214,7 +1214,7 @@ void GLGSRender::notify_tile_unbound(u32 tile) if (false) { u32 addr = rsx::get_address(tiles[tile].offset, tiles[tile].location); - on_notify_memory_unmapped(addr, tiles[tile].size); + on_notify_pre_memory_unmapped(addr, tiles[tile].size); m_rtts.invalidate_surface_address(addr, false); } diff --git a/rpcs3/Emu/RSX/NV47/HW/nv406e.cpp b/rpcs3/Emu/RSX/NV47/HW/nv406e.cpp index 4d43dcd37d..3c6d6de4e2 100644 --- a/rpcs3/Emu/RSX/NV47/HW/nv406e.cpp +++ b/rpcs3/Emu/RSX/NV47/HW/nv406e.cpp @@ -44,7 +44,7 @@ namespace rsx RSX(ctx)->flush_fifo(); } - u64 start = rsx::uclock(); + u64 start = get_system_time(); u64 last_check_val = start; while (sema != arg) @@ -57,7 +57,7 @@ namespace rsx if (const auto tdr = static_cast(g_cfg.video.driver_recovery_timeout)) { - const u64 current = rsx::uclock(); + const u64 current = get_system_time(); if (current - last_check_val > 20'000) { @@ -81,7 +81,7 @@ namespace rsx } RSX(ctx)->fifo_wake_delay(); - RSX(ctx)->performance_counters.idle_time += (rsx::uclock() - start); + RSX(ctx)->performance_counters.idle_time += (get_system_time() - start); } void semaphore_release(context* ctx, u32 /*reg*/, u32 arg) diff --git a/rpcs3/Emu/RSX/Overlays/overlay_animated_icon.cpp b/rpcs3/Emu/RSX/Overlays/overlay_animated_icon.cpp index 00f1e1bd13..d1af06bab2 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_animated_icon.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_animated_icon.cpp @@ -25,11 +25,11 @@ namespace rsx { if (m_last_update_timestamp_us == 0) { - m_last_update_timestamp_us = rsx::uclock(); + m_last_update_timestamp_us = get_system_time(); } else { - const auto now = rsx::uclock(); + const auto now = get_system_time(); m_current_frame_duration_us += (now - m_last_update_timestamp_us); m_last_update_timestamp_us = now; } diff --git a/rpcs3/Emu/RSX/Overlays/overlay_message.cpp b/rpcs3/Emu/RSX/Overlays/overlay_message.cpp index 25a331d0df..5f794797df 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_message.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_message.cpp @@ -13,7 +13,7 @@ namespace rsx return duration; } - return rsx::uclock() + duration; + return get_system_time() + duration; } template @@ -168,7 +168,7 @@ namespace rsx void message::update_queue(std::deque& vis_set, std::deque& ready_set, message_pin_location origin) { - const u64 cur_time = rsx::uclock(); + const u64 cur_time = get_system_time(); for (auto it = vis_set.begin(); it != vis_set.end();) { diff --git a/rpcs3/Emu/RSX/Overlays/overlays.cpp b/rpcs3/Emu/RSX/Overlays/overlays.cpp index c87105547f..e118b3a68c 100644 --- a/rpcs3/Emu/RSX/Overlays/overlays.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlays.cpp @@ -499,7 +499,7 @@ namespace rsx } if (auto rsxthr = rsx::get_current_renderer(); rsxthr && - (min_refresh_duration_us + rsxthr->last_host_flip_timestamp) < rsx::uclock()) + (min_refresh_duration_us + rsxthr->last_host_flip_timestamp) < get_system_time()) { rsxthr->async_flip_requested |= rsx::thread::flip_request::native_ui; } diff --git a/rpcs3/Emu/RSX/RSXFIFO.cpp b/rpcs3/Emu/RSX/RSXFIFO.cpp index ae97509be4..839144a754 100644 --- a/rpcs3/Emu/RSX/RSXFIFO.cpp +++ b/rpcs3/Emu/RSX/RSXFIFO.cpp @@ -173,10 +173,10 @@ namespace rsx break; } - start_time = rsx::uclock(); + start_time = get_system_time(); } - auto now = rsx::uclock(); + auto now = get_system_time(); if (now - start_time >= 50u) { if (m_thread->is_stopped()) @@ -186,7 +186,7 @@ namespace rsx m_thread->cpu_wait({}); - const auto then = std::exchange(now, rsx::uclock()); + const auto then = std::exchange(now, get_system_time()); start_time = now; m_thread->performance_counters.idle_time += now - then; } @@ -623,7 +623,7 @@ namespace rsx { if (performance_counters.state == FIFO::state::running) { - performance_counters.FIFO_idle_timestamp = rsx::uclock(); + performance_counters.FIFO_idle_timestamp = get_system_time(); performance_counters.state = FIFO::state::nop; } @@ -633,7 +633,7 @@ namespace rsx { if (performance_counters.state == FIFO::state::running) { - performance_counters.FIFO_idle_timestamp = rsx::uclock(); + performance_counters.FIFO_idle_timestamp = get_system_time(); performance_counters.state = FIFO::state::empty; } else @@ -668,7 +668,7 @@ namespace rsx //Jump to self. Often preceded by NOP if (performance_counters.state == FIFO::state::running) { - performance_counters.FIFO_idle_timestamp = rsx::uclock(); + performance_counters.FIFO_idle_timestamp = get_system_time(); sync_point_request.release(true); } @@ -749,7 +749,7 @@ namespace rsx } // Update performance counters with time spent in idle mode - performance_counters.idle_time += (rsx::uclock() - performance_counters.FIFO_idle_timestamp); + performance_counters.idle_time += (get_system_time() - performance_counters.FIFO_idle_timestamp); } do diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index fb3a1e2975..8585cfe8e6 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -1024,7 +1024,7 @@ namespace rsx fifo_ctrl = std::make_unique<::rsx::FIFO::FIFO_control>(this); fifo_ctrl->set_get(ctrl->get); - last_guest_flip_timestamp = rsx::uclock() - 1000000; + last_guest_flip_timestamp = get_system_time() - 1000000; vblank_count = 0; @@ -1104,7 +1104,7 @@ namespace rsx if (Emu.IsPaused()) { // Save the difference before pause - start_time = rsx::uclock() - start_time; + start_time = get_system_time() - start_time; while (Emu.IsPaused() && !is_stopped()) { @@ -1112,7 +1112,7 @@ namespace rsx } // Restore difference - start_time = rsx::uclock() - start_time; + start_time = get_system_time() - start_time; } } }))); @@ -3057,7 +3057,7 @@ namespace rsx } } - last_host_flip_timestamp = rsx::uclock(); + last_host_flip_timestamp = get_system_time(); } void thread::check_zcull_status(bool framebuffer_swap) @@ -3299,7 +3299,7 @@ namespace rsx { bool kill_itself = g_cfg.core.rsx_fifo_accuracy == rsx_fifo_mode::as_ps3; - const u64 current_time = rsx::uclock(); + const u64 current_time = get_system_time(); if (recovered_fifo_cmds_history.size() == 20u) { @@ -3381,7 +3381,7 @@ namespace rsx // Some cases do not need full delay remaining = utils::aligned_div(remaining, div); - const u64 until = rsx::uclock() + remaining; + const u64 until = get_system_time() + remaining; while (true) { @@ -3412,7 +3412,7 @@ namespace rsx busy_wait(100); } - const u64 current = rsx::uclock(); + const u64 current = get_system_time(); if (current >= until) { @@ -3508,7 +3508,40 @@ namespace rsx } } - void thread::on_notify_memory_unmapped(u32 address, u32 size) + void thread::on_notify_pre_memory_unmapped(u32 address, u32 size) + { + if (rsx_thread_running && address < rsx::constants::local_mem_base) + { + // Pause RSX thread momentarily to handle unmapping + eng_lock elock(this); + + // Queue up memory invalidation + std::lock_guard lock(m_mtx_task); + const bool existing_range_valid = m_invalidated_memory_range.valid(); + const auto unmap_range = address_range::start_length(address, size); + + if (existing_range_valid && m_invalidated_memory_range.touches(unmap_range)) + { + // Merge range-to-invalidate in case of consecutive unmaps + m_invalidated_memory_range.set_min_max(unmap_range); + } + else + { + if (existing_range_valid) + { + // We can only delay consecutive unmaps. + // Otherwise, to avoid VirtualProtect failures, we need to do the invalidation here + handle_invalidated_memory_range(); + } + + m_invalidated_memory_range = unmap_range; + } + + m_eng_interrupt_mask |= rsx::memory_config_interrupt; + } + } + + void thread::on_notify_post_memory_unmapped(u32 address, u32 size) { if (rsx_thread_running && address < rsx::constants::local_mem_base) { @@ -3559,33 +3592,6 @@ namespace rsx } } } - - // Pause RSX thread momentarily to handle unmapping - eng_lock elock(this); - - // Queue up memory invalidation - std::lock_guard lock(m_mtx_task); - const bool existing_range_valid = m_invalidated_memory_range.valid(); - const auto unmap_range = address_range::start_length(address, size); - - if (existing_range_valid && m_invalidated_memory_range.touches(unmap_range)) - { - // Merge range-to-invalidate in case of consecutive unmaps - m_invalidated_memory_range.set_min_max(unmap_range); - } - else - { - if (existing_range_valid) - { - // We can only delay consecutive unmaps. - // Otherwise, to avoid VirtualProtect failures, we need to do the invalidation here - handle_invalidated_memory_range(); - } - - m_invalidated_memory_range = unmap_range; - } - - m_eng_interrupt_mask |= rsx::memory_config_interrupt; } } @@ -3654,7 +3660,7 @@ namespace rsx //Average load over around 30 frames if (!performance_counters.last_update_timestamp || performance_counters.sampled_frames > 30) { - const auto timestamp = rsx::uclock(); + const auto timestamp = get_system_time(); const auto idle = performance_counters.idle_time.load(); const auto elapsed = timestamp - performance_counters.last_update_timestamp; @@ -3938,7 +3944,7 @@ namespace rsx flip(m_queued_flip); - last_guest_flip_timestamp = rsx::uclock() - 1000000; + last_guest_flip_timestamp = get_system_time() - 1000000; flip_status = CELL_GCM_DISPLAY_FLIP_STATUS_DONE; m_queued_flip.in_progress = false; diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index d0c6c99f3c..88a844d743 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -499,11 +499,17 @@ namespace rsx */ void on_notify_memory_mapped(u32 address_base, u32 size); + /** + * Notify that a section of memory is to be unmapped + * Any data held in the defined range is discarded + */ + void on_notify_pre_memory_unmapped(u32 address_base, u32 size); + /** * Notify that a section of memory has been unmapped * Any data held in the defined range is discarded */ - void on_notify_memory_unmapped(u32 address_base, u32 size); + void on_notify_post_memory_unmapped(u32 address_base, u32 size); /** * Notify to check internal state during semaphore wait diff --git a/rpcs3/Emu/RSX/RSXZCULL.cpp b/rpcs3/Emu/RSX/RSXZCULL.cpp index d1a0b07806..cb9592fb0e 100644 --- a/rpcs3/Emu/RSX/RSXZCULL.cpp +++ b/rpcs3/Emu/RSX/RSXZCULL.cpp @@ -542,7 +542,7 @@ namespace rsx } } - if (m_tsc = rsx::uclock(); m_tsc < m_next_tsc) + if (m_tsc = get_system_time(); m_tsc < m_next_tsc) { return; } diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index c436efe672..3fb1ae0d4e 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -1257,7 +1257,7 @@ void VKGSRender::notify_tile_unbound(u32 tile) if (false) { u32 addr = rsx::get_address(tiles[tile].offset, tiles[tile].location); - on_notify_memory_unmapped(addr, tiles[tile].size); + on_notify_pre_memory_unmapped(addr, tiles[tile].size); m_rtts.invalidate_surface_address(addr, false); } @@ -1892,7 +1892,7 @@ void VKGSRender::sync_hint(rsx::FIFO::interrupt_hint hint, rsx::reports::sync_hi // OK, cell will be accessing the results, probably. // Try to avoid flush spam, it is more costly to flush the CB than it is to just upload the vertex data // This is supposed to be an optimization afterall. - const auto now = rsx::uclock(); + const auto now = get_system_time(); if ((now - m_last_cond_render_eval_hint) > 50) { // Schedule a sync on the next loop iteration diff --git a/rpcs3/main.cpp b/rpcs3/main.cpp index a6f7acf285..1c2e73ecf9 100644 --- a/rpcs3/main.cpp +++ b/rpcs3/main.cpp @@ -569,9 +569,6 @@ int main(int argc, char** argv) ensure(thread_ctrl::is_main(), "Not main thread"); - // Initialize TSC freq (in case it isn't) - static_cast(utils::ensure_tsc_freq_init()); - // Initialize thread pool finalizer (on first use) static_cast(named_thread("", [](int) {})); diff --git a/rpcs3/util/sysinfo.cpp b/rpcs3/util/sysinfo.cpp index f8e122a4e9..d64a5e6553 100755 --- a/rpcs3/util/sysinfo.cpp +++ b/rpcs3/util/sysinfo.cpp @@ -22,6 +22,8 @@ #endif #endif +#include + #include "util/asm.hpp" #include "util/fence.hpp" @@ -734,9 +736,24 @@ bool utils::get_low_power_mode() #endif } -static constexpr ullong round_tsc(ullong val) +static constexpr ullong round_tsc(ullong val, ullong known_error) { - return utils::rounded_div(val, 100'000) * 100'000; + if (known_error >= 500'000) + { + // Do not accept large errors + return 0; + } + + ullong by = 1000; + known_error /= 1000; + + while (known_error && by < 100'000) + { + by *= 10; + known_error /= 10; + } + + return utils::rounded_div(val, by) * by; } namespace utils @@ -744,7 +761,7 @@ namespace utils u64 s_tsc_freq = 0; } -named_thread> s_thread_evaluate_tsc_freq("TSC Evaluate Thread", []() +static const bool s_tsc_freq_evaluated = []() -> bool { static const ullong cal_tsc = []() -> ullong { @@ -763,14 +780,14 @@ named_thread> s_thread_evaluate_tsc_freq("TSC Evaluate Thr return 0; if (freq.QuadPart <= 9'999'999) - return round_tsc(freq.QuadPart * 1024); + return 0; const ullong timer_freq = freq.QuadPart; #else constexpr ullong timer_freq = 1'000'000'000; #endif - constexpr u64 retry_count = 1000; + constexpr u64 retry_count = 1024; // First is entry is for the onset measurements, last is for the end measurements constexpr usz sample_count = 2; @@ -788,6 +805,8 @@ named_thread> s_thread_evaluate_tsc_freq("TSC Evaluate Thr const ullong sec_base = ts0.tv_sec; #endif + constexpr usz sleep_time_ms = 40; + for (usz sample = 0; sample < sample_count; sample++) { for (usz i = 0; i < retry_count; i++) @@ -815,19 +834,34 @@ named_thread> s_thread_evaluate_tsc_freq("TSC Evaluate Thr rdtsc_diff[sample] = rdtsc_read2 >= rdtsc_read ? rdtsc_read2 - rdtsc_read : u64{umax}; } - if (rdtsc_read2 - rdtsc_read < std::min(i, 300) && rdtsc_read2 >= rdtsc_read) + // 80 results in an error range of 4000 hertz (0.00025% of 4GHz CPU, quite acceptable) + // Error of 2.5 seconds per month + if (rdtsc_read2 - rdtsc_read < 80 && rdtsc_read2 >= rdtsc_read) { break; } + + // 8 yields seems to reduce significantly thread contention, improving accuracy + // Even 3 seem to do the job though, but just in case + if (i % 128 == 64) + { + std::this_thread::yield(); + } + + // Take 50% more yields with the last sample because it helps accuracy additionally the more time that passes + if (sample == sample_count - 1 && i % 256 == 128) + { + std::this_thread::yield(); + } } if (sample < sample_count - 1) { - // Sleep 20ms between first and last sample + // Sleep between first and last sample #ifdef _WIN32 - Sleep(20); + Sleep(sleep_time_ms); #else - usleep(20'000); + usleep(sleep_time_ms * 1000); #endif } } @@ -843,17 +877,12 @@ named_thread> s_thread_evaluate_tsc_freq("TSC Evaluate Thr const u64 res = utils::udiv128(static_cast(data >> 64), static_cast(data), (timer_data[1] - timer_data[0])); // Rounding - return round_tsc(res); + return round_tsc(res, utils::mul_saturate(utils::add_saturate(rdtsc_diff[0], rdtsc_diff[1]), utils::aligned_div(timer_freq, timer_data[1] - timer_data[0]))); }(); atomic_storage::release(utils::s_tsc_freq, cal_tsc); -}); - -void utils::ensure_tsc_freq_init() -{ - // Join thread - s_thread_evaluate_tsc_freq(); -} + return true; +}(); u64 utils::get_total_memory() { diff --git a/rpcs3/util/sysinfo.hpp b/rpcs3/util/sysinfo.hpp index 5c38d433f0..4081b3a9cf 100755 --- a/rpcs3/util/sysinfo.hpp +++ b/rpcs3/util/sysinfo.hpp @@ -94,6 +94,4 @@ namespace utils { return s_tsc_freq; } - - void ensure_tsc_freq_init(); }