mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-08-03 06:40:18 +00:00
Merge branch 'master' into myfix
This commit is contained in:
commit
37bf1acbbe
23 changed files with 305 additions and 119 deletions
|
@ -19,7 +19,7 @@ The following tools are required to build RPCS3 on Windows 10 or later:
|
||||||
with standalone **CMake** tool.
|
with standalone **CMake** tool.
|
||||||
|
|
||||||
- [Python 3.6+](https://www.python.org/downloads/) (add to PATH)
|
- [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.
|
- [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.
|
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.
|
||||||
|
|
|
@ -2475,7 +2475,6 @@ bool fs::pending_file::commit(bool overwrite)
|
||||||
{
|
{
|
||||||
file.sync();
|
file.sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
@ -2486,15 +2485,98 @@ bool fs::pending_file::commit(bool overwrite)
|
||||||
disp.DeleteFileW = false;
|
disp.DeleteFileW = false;
|
||||||
ensure(SetFileInformationByHandle(file.get_handle(), FileDispositionInfo, &disp, sizeof(disp)));
|
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
|
#endif
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
const auto ws1 = to_wchar(m_path);
|
|
||||||
const auto ws2 = to_wchar(m_dest);
|
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
|
// Disable the destructor
|
||||||
m_path.clear();
|
m_path.clear();
|
||||||
|
|
|
@ -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);
|
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);
|
std::lock_guard lock(mutex);
|
||||||
|
|
||||||
if (!exists)
|
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;
|
std::tie(ppu.gpr[4], ppu.gpr[5], ppu.gpr[6], ppu.gpr[7]) = event;
|
||||||
|
|
||||||
awake(&ppu);
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -709,7 +723,10 @@ error_code sys_event_port_disconnect(ppu_thread& ppu, u32 eport_id)
|
||||||
return CELL_ENOTCONN;
|
return CELL_ENOTCONN;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: return CELL_EBUSY if necessary (can't detect the condition)
|
if (port->is_busy)
|
||||||
|
{
|
||||||
|
return CELL_EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
port->queue.reset();
|
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)
|
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;
|
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);
|
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
|
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))
|
if (lv2_obj::check(port.queue))
|
||||||
{
|
{
|
||||||
const u64 source = port.name ? port.name : (u64{process_getpid() + 0u} << 32) | u64{eport_id};
|
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;
|
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;
|
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)
|
||||||
{
|
{
|
||||||
if (port.ret == CELL_EAGAIN)
|
if (port.ret == CELL_EAGAIN)
|
||||||
|
|
|
@ -79,6 +79,8 @@ struct sys_event_t
|
||||||
// Source, data1, data2, data3
|
// Source, data1, data2, data3
|
||||||
using lv2_event = std::tuple<u64, u64, u64, u64>;
|
using lv2_event = std::tuple<u64, u64, u64, u64>;
|
||||||
|
|
||||||
|
struct lv2_event_port;
|
||||||
|
|
||||||
struct lv2_event_queue final : public lv2_obj
|
struct lv2_event_queue final : public lv2_obj
|
||||||
{
|
{
|
||||||
static const u32 id_base = 0x8d000000;
|
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 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 = {});
|
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
|
// 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 s32 type; // Port type, either IPC or local
|
||||||
const u64 name; // Event source (generated from id and process id if not set)
|
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
|
std::shared_ptr<lv2_event_queue> queue; // Event queue this port is connected to
|
||||||
|
|
||||||
lv2_event_port(s32 type, u64 name)
|
lv2_event_port(s32 type, u64 name)
|
||||||
|
|
|
@ -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)
|
auto [new_val, ok] = flag->pattern.fetch_op([&](u64& x)
|
||||||
{
|
{
|
||||||
if ((x ^ pattern) & dependant_mask)
|
if ((x ^ pattern) & dependant_mask)
|
||||||
|
|
|
@ -212,9 +212,9 @@ u64 get_system_time()
|
||||||
const u64 tsc = utils::get_tsc();
|
const u64 tsc = utils::get_tsc();
|
||||||
|
|
||||||
#if _MSC_VER
|
#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
|
#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
|
#endif
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1009,7 +1009,7 @@ namespace vm
|
||||||
// the RSX might try to call VirtualProtect on memory that is already unmapped
|
// the RSX might try to call VirtualProtect on memory that is already unmapped
|
||||||
if (auto rsxthr = g_fxo->try_get<rsx::thread>())
|
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
|
// 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)();
|
auto& m_map = (m.*block_map)();
|
||||||
|
|
||||||
|
@ -1320,7 +1320,13 @@ namespace vm
|
||||||
{
|
{
|
||||||
const auto next = std::next(it);
|
const auto next = std::next(it);
|
||||||
const auto size = it->second.first;
|
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;
|
it = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1480,6 +1486,20 @@ namespace vm
|
||||||
{
|
{
|
||||||
auto& m_map = (m.*block_map)();
|
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;
|
vm::writer_lock lock;
|
||||||
|
|
||||||
const auto found = m_map.find(addr - (flags & stack_guarded ? 0x1000 : 0));
|
const auto found = m_map.find(addr - (flags & stack_guarded ? 0x1000 : 0));
|
||||||
|
@ -1517,6 +1537,8 @@ namespace vm
|
||||||
// Remove entry
|
// Remove entry
|
||||||
m_map.erase(found);
|
m_map.erase(found);
|
||||||
|
|
||||||
|
unmap_notification.size = size;
|
||||||
|
unmap_notification.addr = addr;
|
||||||
return size;
|
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)
|
static bool _test_map(u32 addr, u32 size)
|
||||||
|
@ -1964,6 +1986,22 @@ namespace vm
|
||||||
|
|
||||||
std::pair<std::shared_ptr<block_t>, bool> result{};
|
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;
|
vm::writer_lock lock;
|
||||||
|
|
||||||
for (auto it = g_locations.begin() + memory_location_max; it != g_locations.end(); it++)
|
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);
|
result.first = std::move(*it);
|
||||||
g_locations.erase(it);
|
g_locations.erase(it);
|
||||||
ensure(_unmap_block(result.first));
|
ensure(_unmap_block(result.first, &unmap_notifications.addr_size_pairs));
|
||||||
result.second = true;
|
result.second = true;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,8 +133,8 @@ namespace vm
|
||||||
bool try_alloc(u32 addr, u64 bflags, u32 size, std::shared_ptr<utils::shm>&&) const;
|
bool try_alloc(u32 addr, u64 bflags, u32 size, std::shared_ptr<utils::shm>&&) const;
|
||||||
|
|
||||||
// Unmap block
|
// Unmap block
|
||||||
bool unmap();
|
bool unmap(std::vector<std::pair<u32, u32>>* unmapped = nullptr);
|
||||||
friend bool _unmap_block(const std::shared_ptr<block_t>&);
|
friend bool _unmap_block(const std::shared_ptr<block_t>&, std::vector<std::pair<u32, u32>>* unmapped);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
block_t(u32 addr, u32 size, u64 flags);
|
block_t(u32 addr, u32 size, u64 flags);
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace rsx
|
||||||
{
|
{
|
||||||
if (enabled) [[unlikely]]
|
if (enabled) [[unlikely]]
|
||||||
{
|
{
|
||||||
last = rsx::uclock();
|
last = get_system_time();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ namespace rsx
|
||||||
}
|
}
|
||||||
|
|
||||||
auto old = last;
|
auto old = last;
|
||||||
last = rsx::uclock();
|
last = get_system_time();
|
||||||
return static_cast<s64>(last - old);
|
return static_cast<s64>(last - old);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,20 +4,3 @@
|
||||||
#include <util/sysinfo.hpp>
|
#include <util/sysinfo.hpp>
|
||||||
|
|
||||||
#include "Emu/Cell/timers.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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1214,7 +1214,7 @@ void GLGSRender::notify_tile_unbound(u32 tile)
|
||||||
if (false)
|
if (false)
|
||||||
{
|
{
|
||||||
u32 addr = rsx::get_address(tiles[tile].offset, tiles[tile].location);
|
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);
|
m_rtts.invalidate_surface_address(addr, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ namespace rsx
|
||||||
RSX(ctx)->flush_fifo();
|
RSX(ctx)->flush_fifo();
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 start = rsx::uclock();
|
u64 start = get_system_time();
|
||||||
u64 last_check_val = start;
|
u64 last_check_val = start;
|
||||||
|
|
||||||
while (sema != arg)
|
while (sema != arg)
|
||||||
|
@ -57,7 +57,7 @@ namespace rsx
|
||||||
|
|
||||||
if (const auto tdr = static_cast<u64>(g_cfg.video.driver_recovery_timeout))
|
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)
|
if (current - last_check_val > 20'000)
|
||||||
{
|
{
|
||||||
|
@ -81,7 +81,7 @@ namespace rsx
|
||||||
}
|
}
|
||||||
|
|
||||||
RSX(ctx)->fifo_wake_delay();
|
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)
|
void semaphore_release(context* ctx, u32 /*reg*/, u32 arg)
|
||||||
|
|
|
@ -25,11 +25,11 @@ namespace rsx
|
||||||
{
|
{
|
||||||
if (m_last_update_timestamp_us == 0)
|
if (m_last_update_timestamp_us == 0)
|
||||||
{
|
{
|
||||||
m_last_update_timestamp_us = rsx::uclock();
|
m_last_update_timestamp_us = get_system_time();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const auto now = rsx::uclock();
|
const auto now = get_system_time();
|
||||||
m_current_frame_duration_us += (now - m_last_update_timestamp_us);
|
m_current_frame_duration_us += (now - m_last_update_timestamp_us);
|
||||||
m_last_update_timestamp_us = now;
|
m_last_update_timestamp_us = now;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace rsx
|
||||||
return duration;
|
return duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
return rsx::uclock() + duration;
|
return get_system_time() + duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
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)
|
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();)
|
for (auto it = vis_set.begin(); it != vis_set.end();)
|
||||||
{
|
{
|
||||||
|
|
|
@ -499,7 +499,7 @@ namespace rsx
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto rsxthr = rsx::get_current_renderer(); rsxthr &&
|
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;
|
rsxthr->async_flip_requested |= rsx::thread::flip_request::native_ui;
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,10 +173,10 @@ namespace rsx
|
||||||
break;
|
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 (now - start_time >= 50u)
|
||||||
{
|
{
|
||||||
if (m_thread->is_stopped())
|
if (m_thread->is_stopped())
|
||||||
|
@ -186,7 +186,7 @@ namespace rsx
|
||||||
|
|
||||||
m_thread->cpu_wait({});
|
m_thread->cpu_wait({});
|
||||||
|
|
||||||
const auto then = std::exchange(now, rsx::uclock());
|
const auto then = std::exchange(now, get_system_time());
|
||||||
start_time = now;
|
start_time = now;
|
||||||
m_thread->performance_counters.idle_time += now - then;
|
m_thread->performance_counters.idle_time += now - then;
|
||||||
}
|
}
|
||||||
|
@ -623,7 +623,7 @@ namespace rsx
|
||||||
{
|
{
|
||||||
if (performance_counters.state == FIFO::state::running)
|
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;
|
performance_counters.state = FIFO::state::nop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -633,7 +633,7 @@ namespace rsx
|
||||||
{
|
{
|
||||||
if (performance_counters.state == FIFO::state::running)
|
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;
|
performance_counters.state = FIFO::state::empty;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -668,7 +668,7 @@ namespace rsx
|
||||||
//Jump to self. Often preceded by NOP
|
//Jump to self. Often preceded by NOP
|
||||||
if (performance_counters.state == FIFO::state::running)
|
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);
|
sync_point_request.release(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -749,7 +749,7 @@ namespace rsx
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update performance counters with time spent in idle mode
|
// 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
|
do
|
||||||
|
|
|
@ -1024,7 +1024,7 @@ namespace rsx
|
||||||
fifo_ctrl = std::make_unique<::rsx::FIFO::FIFO_control>(this);
|
fifo_ctrl = std::make_unique<::rsx::FIFO::FIFO_control>(this);
|
||||||
fifo_ctrl->set_get(ctrl->get);
|
fifo_ctrl->set_get(ctrl->get);
|
||||||
|
|
||||||
last_guest_flip_timestamp = rsx::uclock() - 1000000;
|
last_guest_flip_timestamp = get_system_time() - 1000000;
|
||||||
|
|
||||||
vblank_count = 0;
|
vblank_count = 0;
|
||||||
|
|
||||||
|
@ -1104,7 +1104,7 @@ namespace rsx
|
||||||
if (Emu.IsPaused())
|
if (Emu.IsPaused())
|
||||||
{
|
{
|
||||||
// Save the difference before pause
|
// Save the difference before pause
|
||||||
start_time = rsx::uclock() - start_time;
|
start_time = get_system_time() - start_time;
|
||||||
|
|
||||||
while (Emu.IsPaused() && !is_stopped())
|
while (Emu.IsPaused() && !is_stopped())
|
||||||
{
|
{
|
||||||
|
@ -1112,7 +1112,7 @@ namespace rsx
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore difference
|
// 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)
|
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;
|
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)
|
if (recovered_fifo_cmds_history.size() == 20u)
|
||||||
{
|
{
|
||||||
|
@ -3381,7 +3381,7 @@ namespace rsx
|
||||||
|
|
||||||
// Some cases do not need full delay
|
// Some cases do not need full delay
|
||||||
remaining = utils::aligned_div(remaining, div);
|
remaining = utils::aligned_div(remaining, div);
|
||||||
const u64 until = rsx::uclock() + remaining;
|
const u64 until = get_system_time() + remaining;
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
@ -3412,7 +3412,7 @@ namespace rsx
|
||||||
busy_wait(100);
|
busy_wait(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
const u64 current = rsx::uclock();
|
const u64 current = get_system_time();
|
||||||
|
|
||||||
if (current >= until)
|
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)
|
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
|
//Average load over around 30 frames
|
||||||
if (!performance_counters.last_update_timestamp || performance_counters.sampled_frames > 30)
|
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 idle = performance_counters.idle_time.load();
|
||||||
const auto elapsed = timestamp - performance_counters.last_update_timestamp;
|
const auto elapsed = timestamp - performance_counters.last_update_timestamp;
|
||||||
|
|
||||||
|
@ -3938,7 +3944,7 @@ namespace rsx
|
||||||
|
|
||||||
flip(m_queued_flip);
|
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;
|
flip_status = CELL_GCM_DISPLAY_FLIP_STATUS_DONE;
|
||||||
m_queued_flip.in_progress = false;
|
m_queued_flip.in_progress = false;
|
||||||
|
|
||||||
|
|
|
@ -499,11 +499,17 @@ namespace rsx
|
||||||
*/
|
*/
|
||||||
void on_notify_memory_mapped(u32 address_base, u32 size);
|
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
|
* Notify that a section of memory has been unmapped
|
||||||
* Any data held in the defined range is discarded
|
* 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
|
* Notify to check internal state during semaphore wait
|
||||||
|
|
|
@ -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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1257,7 +1257,7 @@ void VKGSRender::notify_tile_unbound(u32 tile)
|
||||||
if (false)
|
if (false)
|
||||||
{
|
{
|
||||||
u32 addr = rsx::get_address(tiles[tile].offset, tiles[tile].location);
|
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);
|
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.
|
// 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
|
// 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.
|
// 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)
|
if ((now - m_last_cond_render_eval_hint) > 50)
|
||||||
{
|
{
|
||||||
// Schedule a sync on the next loop iteration
|
// Schedule a sync on the next loop iteration
|
||||||
|
|
|
@ -569,9 +569,6 @@ int main(int argc, char** argv)
|
||||||
|
|
||||||
ensure(thread_ctrl::is_main(), "Not main thread");
|
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)
|
// Initialize thread pool finalizer (on first use)
|
||||||
static_cast<void>(named_thread("", [](int) {}));
|
static_cast<void>(named_thread("", [](int) {}));
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
#include "util/asm.hpp"
|
#include "util/asm.hpp"
|
||||||
#include "util/fence.hpp"
|
#include "util/fence.hpp"
|
||||||
|
|
||||||
|
@ -734,9 +736,24 @@ bool utils::get_low_power_mode()
|
||||||
#endif
|
#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
|
namespace utils
|
||||||
|
@ -744,7 +761,7 @@ namespace utils
|
||||||
u64 s_tsc_freq = 0;
|
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
|
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;
|
return 0;
|
||||||
|
|
||||||
if (freq.QuadPart <= 9'999'999)
|
if (freq.QuadPart <= 9'999'999)
|
||||||
return round_tsc(freq.QuadPart * 1024);
|
return 0;
|
||||||
|
|
||||||
const ullong timer_freq = freq.QuadPart;
|
const ullong timer_freq = freq.QuadPart;
|
||||||
#else
|
#else
|
||||||
constexpr ullong timer_freq = 1'000'000'000;
|
constexpr ullong timer_freq = 1'000'000'000;
|
||||||
#endif
|
#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
|
// First is entry is for the onset measurements, last is for the end measurements
|
||||||
constexpr usz sample_count = 2;
|
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;
|
const ullong sec_base = ts0.tv_sec;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
constexpr usz sleep_time_ms = 40;
|
||||||
|
|
||||||
for (usz sample = 0; sample < sample_count; sample++)
|
for (usz sample = 0; sample < sample_count; sample++)
|
||||||
{
|
{
|
||||||
for (usz i = 0; i < retry_count; i++)
|
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};
|
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;
|
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)
|
if (sample < sample_count - 1)
|
||||||
{
|
{
|
||||||
// Sleep 20ms between first and last sample
|
// Sleep between first and last sample
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
Sleep(20);
|
Sleep(sleep_time_ms);
|
||||||
#else
|
#else
|
||||||
usleep(20'000);
|
usleep(sleep_time_ms * 1000);
|
||||||
#endif
|
#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]));
|
const u64 res = utils::udiv128(static_cast<u64>(data >> 64), static_cast<u64>(data), (timer_data[1] - timer_data[0]));
|
||||||
|
|
||||||
// Rounding
|
// 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);
|
atomic_storage<u64>::release(utils::s_tsc_freq, cal_tsc);
|
||||||
});
|
return true;
|
||||||
|
}();
|
||||||
void utils::ensure_tsc_freq_init()
|
|
||||||
{
|
|
||||||
// Join thread
|
|
||||||
s_thread_evaluate_tsc_freq();
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 utils::get_total_memory()
|
u64 utils::get_total_memory()
|
||||||
{
|
{
|
||||||
|
|
|
@ -94,6 +94,4 @@ namespace utils
|
||||||
{
|
{
|
||||||
return s_tsc_freq;
|
return s_tsc_freq;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ensure_tsc_freq_init();
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue