Merge branch 'master' into myfix

This commit is contained in:
Megamouse 2024-11-01 07:33:30 +01:00 committed by GitHub
commit 37bf1acbbe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 305 additions and 119 deletions

View file

@ -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.

View file

@ -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<std::wstring> 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<DWORD>(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<DWORD>(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();

View file

@ -119,8 +119,13 @@ std::shared_ptr<lv2_event_queue> 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<ppu_thread>())->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<ppu_thread>() : 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<lv2_obj, lv2_event_port>(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, &notified_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)

View file

@ -79,6 +79,8 @@ struct sys_event_t
// Source, data1, data2, data3
using lv2_event = std::tuple<u64, u64, u64, u64>;
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<lv2_event_queue> load_ptr(utils::serial& ar, std::shared_ptr<lv2_event_queue>& 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<usz> is_busy = 0; // Counts threads waiting on event sending
std::shared_ptr<lv2_event_queue> queue; // Event queue this port is connected to
lv2_event_port(s32 type, u64 name)

View file

@ -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)

View file

@ -212,9 +212,9 @@ u64 get_system_time()
const u64 tsc = utils::get_tsc();
#if _MSC_VER
const u64 result = static_cast<u64>(u128_from_mul(tsc, 1000000ull) / freq) * g_cfg.core.clocks_scale / 100u;
const u64 result = static_cast<u64>(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;
}

View file

@ -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<rsx::thread>())
{
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<std::pair<u32, u32>>* 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<rsx::thread>(); 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_t>& block)
bool _unmap_block(const std::shared_ptr<block_t>& block, std::vector<std::pair<u32, u32>>* 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<std::shared_ptr<block_t>, bool> result{};
struct notify_t
{
std::vector<std::pair<u32, u32>> addr_size_pairs;
~notify_t() noexcept
{
for (const auto [addr, size] : addr_size_pairs)
{
if (auto rsxthr = g_fxo->try_get<rsx::thread>())
{
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;
}

View file

@ -133,8 +133,8 @@ namespace vm
bool try_alloc(u32 addr, u64 bflags, u32 size, std::shared_ptr<utils::shm>&&) const;
// Unmap block
bool unmap();
friend bool _unmap_block(const std::shared_ptr<block_t>&);
bool unmap(std::vector<std::pair<u32, u32>>* unmapped = nullptr);
friend bool _unmap_block(const std::shared_ptr<block_t>&, std::vector<std::pair<u32, u32>>* unmapped);
public:
block_t(u32 addr, u32 size, u64 flags);

View file

@ -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<s64>(last - old);
}
};

View file

@ -4,20 +4,3 @@
#include <util/sysinfo.hpp>
#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();
}
}
}

View file

@ -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);
}

View file

@ -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<u64>(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)

View file

@ -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;
}

View file

@ -13,7 +13,7 @@ namespace rsx
return duration;
}
return rsx::uclock() + duration;
return get_system_time() + duration;
}
template <typename T>
@ -168,7 +168,7 @@ namespace rsx
void message::update_queue(std::deque<message_item>& vis_set, std::deque<message_item>& 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();)
{

View file

@ -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;
}

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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<void>(utils::ensure_tsc_freq_init());
// Initialize thread pool finalizer (on first use)
static_cast<void>(named_thread("", [](int) {}));

View file

@ -22,6 +22,8 @@
#endif
#endif
#include <thread>
#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<std::function<void()>> 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<std::function<void()>> 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<std::function<void()>> 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<std::function<void()>> 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<usz>(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<std::function<void()>> s_thread_evaluate_tsc_freq("TSC Evaluate Thr
const u64 res = utils::udiv128(static_cast<u64>(data >> 64), static_cast<u64>(data), (timer_data[1] - timer_data[0]));
// Rounding
return round_tsc(res);
return round_tsc(res, utils::mul_saturate<u64>(utils::add_saturate<u64>(rdtsc_diff[0], rdtsc_diff[1]), utils::aligned_div(timer_freq, timer_data[1] - timer_data[0])));
}();
atomic_storage<u64>::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()
{

View file

@ -94,6 +94,4 @@ namespace utils
{
return s_tsc_freq;
}
void ensure_tsc_freq_init();
}