Devtools: Pause system

This commit is contained in:
Vinicius Rangel 2024-09-26 20:20:57 -03:00
parent 009f956d8d
commit 983cafb5f2
No known key found for this signature in database
GPG Key ID: A5B154D904B761D9
11 changed files with 257 additions and 61 deletions

View File

@ -458,6 +458,8 @@ set(CORE src/core/aerolib/stubs.cpp
src/core/platform.h src/core/platform.h
src/core/signals.cpp src/core/signals.cpp
src/core/signals.h src/core/signals.h
src/core/system.cpp
src/core/system.h
src/core/tls.cpp src/core/tls.cpp
src/core/tls.h src/core/tls.h
src/core/virtual_memory.cpp src/core/virtual_memory.cpp

View File

@ -17,6 +17,7 @@
#include "core/libraries/kernel/threads/threads.h" #include "core/libraries/kernel/threads/threads.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
#include "core/linker.h" #include "core/linker.h"
#include "core/system.h"
#include "core/tls.h" #include "core/tls.h"
#ifdef _WIN64 #ifdef _WIN64
#include <windows.h> #include <windows.h>
@ -988,6 +989,7 @@ static void cleanup_thread(void* arg) {
} }
Core::SetTcbBase(nullptr); Core::SetTcbBase(nullptr);
thread->is_almost_done = true; thread->is_almost_done = true;
Common::Singleton<SystemState>::Instance()->RemoveCurrentThreadFromGuestList();
} }
static void* run_thread(void* arg) { static void* run_thread(void* arg) {
@ -998,6 +1000,7 @@ static void* run_thread(void* arg) {
g_pthread_self = thread; g_pthread_self = thread;
pthread_cleanup_push(cleanup_thread, thread); pthread_cleanup_push(cleanup_thread, thread);
thread->is_started = true; thread->is_started = true;
Common::Singleton<SystemState>::Instance()->AddCurrentThreadToGuestList();
ret = linker->ExecuteGuest(thread->entry, thread->arg); ret = linker->ExecuteGuest(thread->entry, thread->arg);
pthread_cleanup_pop(1); pthread_cleanup_pop(1);
return ret; return ret;

View File

@ -247,6 +247,17 @@ int PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2,
return SCE_OK; return SCE_OK;
} }
namespace Dev {
u64& GetInitialPtc() {
return initial_ptc;
}
Common::NativeClock* GetClock() {
return clock.get();
}
} // namespace Dev
void timeSymbolsRegister(Core::Loader::SymbolsResolver* sym) { void timeSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
clock = std::make_unique<Common::NativeClock>(); clock = std::make_unique<Common::NativeClock>();
initial_ptc = clock->GetUptime(); initial_ptc = clock->GetUptime();

View File

@ -7,6 +7,10 @@
#include "common/types.h" #include "common/types.h"
namespace Common {
class NativeClock;
}
namespace Core::Loader { namespace Core::Loader {
class SymbolsResolver; class SymbolsResolver;
} }
@ -47,6 +51,12 @@ constexpr int ORBIS_CLOCK_EXT_DEBUG_NETWORK = 17;
constexpr int ORBIS_CLOCK_EXT_AD_NETWORK = 18; constexpr int ORBIS_CLOCK_EXT_AD_NETWORK = 18;
constexpr int ORBIS_CLOCK_EXT_RAW_NETWORK = 19; constexpr int ORBIS_CLOCK_EXT_RAW_NETWORK = 19;
namespace Dev {
u64& GetInitialPtc();
Common::NativeClock* GetClock();
} // namespace Dev
u64 PS4_SYSV_ABI sceKernelGetTscFrequency(); u64 PS4_SYSV_ABI sceKernelGetTscFrequency();
u64 PS4_SYSV_ABI sceKernelGetProcessTime(); u64 PS4_SYSV_ABI sceKernelGetProcessTime();
u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounter(); u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounter();

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <thread> #include <thread>
#include <utility>
#include <imgui.h> #include <imgui.h>
#include "common/assert.h" #include "common/assert.h"
@ -281,10 +282,15 @@ void MsgDialogUi::Draw() {
first_render = false; first_render = false;
} }
DialogResult Libraries::MsgDialog::ShowMsgDialog(MsgDialogState state, bool block) { DialogResult Libraries::MsgDialog::ShowMsgDialog(MsgDialogState p_state, bool block) {
DialogResult result{}; static DialogResult result{};
Status status = Status::RUNNING; static Status status;
MsgDialogUi dialog(&state, &status, &result); static MsgDialogUi dialog;
static MsgDialogState state;
dialog = MsgDialogUi{};
status = Status::RUNNING;
state = std::move(p_state);
dialog = MsgDialogUi(&state, &status, &result);
if (block) { if (block) {
while (status == Status::RUNNING) { while (status == Status::RUNNING) {
std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::this_thread::sleep_for(std::chrono::milliseconds(100));

View File

@ -12,6 +12,7 @@
#include "core/libraries/kernel/time_management.h" #include "core/libraries/kernel/time_management.h"
#include "core/libraries/videoout/driver.h" #include "core/libraries/videoout/driver.h"
#include "core/platform.h" #include "core/platform.h"
#include "core/system.h"
#include "video_core/renderer_vulkan/renderer_vulkan.h" #include "video_core/renderer_vulkan/renderer_vulkan.h"
extern std::unique_ptr<Vulkan::RendererVulkan> renderer; extern std::unique_ptr<Vulkan::RendererVulkan> renderer;
@ -265,6 +266,8 @@ void VideoOutDriver::PresentThread(std::stop_token token) {
Common::AccurateTimer timer{vblank_period}; Common::AccurateTimer timer{vblank_period};
auto systemState = Common::Singleton<SystemState>::Instance();
const auto receive_request = [this] -> Request { const auto receive_request = [this] -> Request {
std::scoped_lock lk{mutex}; std::scoped_lock lk{mutex};
if (!requests.empty()) { if (!requests.empty()) {
@ -284,7 +287,7 @@ void VideoOutDriver::PresentThread(std::stop_token token) {
if (vblank_status.count % (main_port.flip_rate + 1) == 0) { if (vblank_status.count % (main_port.flip_rate + 1) == 0) {
const auto request = receive_request(); const auto request = receive_request();
if (!request) { if (!request) {
if (!main_port.is_open) { if (!main_port.is_open || systemState->IsGuestThreadsPaused()) {
DrawBlankFrame(); DrawBlankFrame();
} }
} else { } else {

View File

@ -18,6 +18,7 @@
#include "core/memory.h" #include "core/memory.h"
#include "core/tls.h" #include "core/tls.h"
#include "core/virtual_memory.h" #include "core/virtual_memory.h"
#include "system.h"
namespace Core { namespace Core {
@ -88,6 +89,7 @@ void Linker::Execute() {
// Init primary thread. // Init primary thread.
Common::SetCurrentThreadName("GAME_MainThread"); Common::SetCurrentThreadName("GAME_MainThread");
Common::Singleton<SystemState>::Instance()->AddCurrentThreadToGuestList();
Libraries::Kernel::pthreadInitSelfMainThread(); Libraries::Kernel::pthreadInitSelfMainThread();
EnsureThreadInitialized(true); EnsureThreadInitialized(true);

View File

@ -83,6 +83,12 @@ static void SignalHandler(int sig, siginfo_t* info, void* raw_context) {
fmt::ptr(code_address), DisassembleInstruction(code_address)); fmt::ptr(code_address), DisassembleInstruction(code_address));
} }
break; break;
case SIGUSR1: { // Sleep thread until signal is received
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGUSR1);
sigwait(&sigset, &sig);
} break;
default: default:
break; break;
} }
@ -105,6 +111,8 @@ SignalDispatch::SignalDispatch() {
"Failed to register access violation signal handler."); "Failed to register access violation signal handler.");
ASSERT_MSG(sigaction(SIGILL, &action, nullptr) == 0, ASSERT_MSG(sigaction(SIGILL, &action, nullptr) == 0,
"Failed to register illegal instruction signal handler."); "Failed to register illegal instruction signal handler.");
ASSERT_MSG(sigaction(SIGUSR1, &action, nullptr) == 0,
"Failed to register sleep signal handler.");
#endif #endif
} }

71
src/core/system.cpp Normal file
View File

@ -0,0 +1,71 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/native_clock.h"
#include "libraries/kernel/event_queues.h"
#include "libraries/kernel/time_management.h"
#include "libraries/system/msgdialog.h"
#include "system.h"
void SystemState::AddCurrentThreadToGuestList() {
std::lock_guard lock{guest_threads_mutex};
ThreadID id;
#ifdef _WIN32
id = GetCurrentThreadId();
#else
id = pthread_self();
#endif
guest_threads.push_back(id);
}
void SystemState::RemoveCurrentThreadFromGuestList() {
std::lock_guard lock{guest_threads_mutex};
ThreadID id;
#ifdef _WIN32
id = GetCurrentThreadId();
#else
id = pthread_self();
#endif
std::erase_if(guest_threads, [&](const ThreadID& v) { return v == id; });
}
void SystemState::PauseGuestThreads() {
using namespace Libraries::MsgDialog;
std::lock_guard lock{guest_threads_mutex};
if (is_guest_threads_paused) {
return;
}
for (const auto& id : guest_threads) {
#ifdef _WIN32
const HANDLE hd = OpenThread(THREAD_SUSPEND_RESUME, FALSE, id);
SuspendThread(hd);
CloseHandle(hd);
#else
pthread_kill(id, SIGUSR1);
#endif
}
pause_time = Libraries::Kernel::Dev::GetClock()->GetUptime();
is_guest_threads_paused = true;
}
void SystemState::ResumeGuestThreads() {
std::lock_guard lock{guest_threads_mutex};
if (!is_guest_threads_paused) {
return;
}
u64 delta_time = Libraries::Kernel::Dev::GetClock()->GetUptime() - pause_time;
Libraries::Kernel::Dev::GetInitialPtc() += delta_time;
for (const auto& id : guest_threads) {
#ifdef _WIN32
const HANDLE hd = OpenThread(THREAD_SUSPEND_RESUME, FALSE, id);
ResumeThread(hd);
CloseHandle(hd);
#else
pthread_kill(id, SIGUSR1);
#endif
}
is_guest_threads_paused = false;
}

39
src/core/system.h Normal file
View File

@ -0,0 +1,39 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <mutex>
#include <vector>
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#include <Windows.h>
using ThreadID = DWORD;
#else
#include <pthread.h>
#include <signal.h>
using ThreadID = pthread_t;
#endif
class SystemState {
std::mutex guest_threads_mutex{};
std::vector<ThreadID> guest_threads{};
bool is_guest_threads_paused = false;
u64 pause_time{};
public:
void AddCurrentThreadToGuestList();
void RemoveCurrentThreadFromGuestList();
void PauseGuestThreads();
void ResumeGuestThreads();
inline bool IsGuestThreadsPaused() const {
return is_guest_threads_paused;
}
};

View File

@ -4,7 +4,9 @@
#include <imgui.h> #include <imgui.h>
#include "common/config.h" #include "common/config.h"
#include "common/singleton.h"
#include "common/types.h" #include "common/types.h"
#include "core/system.h"
#include "imgui_internal.h" #include "imgui_internal.h"
#include "video_info.h" #include "video_info.h"
@ -18,7 +20,11 @@ struct FrameInfo {
static bool show = false; static bool show = false;
static bool show_advanced = false; static bool show_advanced = false;
static auto sysState = Common::Singleton<SystemState>::Instance();
static u32 current_frame = 0; static u32 current_frame = 0;
namespace FrameGraph {
constexpr float TARGET_FPS = 60.0f; constexpr float TARGET_FPS = 60.0f;
constexpr u32 FRAME_BUFFER_SIZE = 1024; constexpr u32 FRAME_BUFFER_SIZE = 1024;
constexpr float BAR_WIDTH_MULT = 1.4f; constexpr float BAR_WIDTH_MULT = 1.4f;
@ -27,78 +33,111 @@ constexpr float FRAME_GRAPH_PADDING_Y = 3.0f;
static std::array<FrameInfo, FRAME_BUFFER_SIZE> frame_list; static std::array<FrameInfo, FRAME_BUFFER_SIZE> frame_list;
static float frame_graph_height = 50.0f; static float frame_graph_height = 50.0f;
static void Draw() {
const auto& ctx = *GImGui;
const auto& window = *ctx.CurrentWindow;
auto& draw_list = *window.DrawList;
const float full_width = GetContentRegionAvail().x;
// Frame graph - inspired by
// https://asawicki.info/news_1758_an_idea_for_visualization_of_frame_times
auto pos = GetCursorScreenPos();
const ImVec2 size{full_width, frame_graph_height + FRAME_GRAPH_PADDING_Y * 2.0f};
ItemSize(size);
if (!ItemAdd({pos, pos + size}, GetID("FrameGraph"))) {
return;
}
float target_dt = 1.0f / (TARGET_FPS * (float)Config::vblankDiv());
float cur_pos_x = pos.x + full_width;
pos.y += FRAME_GRAPH_PADDING_Y;
const float final_pos_y = pos.y + frame_graph_height;
draw_list.AddRectFilled({pos.x, pos.y - FRAME_GRAPH_PADDING_Y},
{pos.x + full_width, final_pos_y + FRAME_GRAPH_PADDING_Y},
IM_COL32(0x33, 0x33, 0x33, 0xFF));
draw_list.PushClipRect({pos.x, pos.y}, {pos.x + full_width, final_pos_y}, true);
for (u32 i = 0; i < FRAME_BUFFER_SIZE; ++i) {
const auto& frame_info = frame_list[(current_frame - i) % FRAME_BUFFER_SIZE];
const float dt_factor = target_dt / frame_info.delta;
const float width = std::ceil(BAR_WIDTH_MULT / dt_factor);
const float height =
std::min(std::log2(BAR_HEIGHT_MULT / dt_factor) / 3.0f, 1.0f) * frame_graph_height;
ImU32 color;
if (dt_factor >= 0.95f) { // BLUE
color = IM_COL32(0x33, 0x33, 0xFF, 0xFF);
} else if (dt_factor >= 0.5f) { // GREEN <> YELLOW
float t = 1.0f - (dt_factor - 0.5f) * 2.0f;
int r = (int)(0xFF * t);
color = IM_COL32(r, 0xFF, 0, 0xFF);
} else { // YELLOW <> RED
float t = dt_factor * 2.0f;
int g = (int)(0xFF * t);
color = IM_COL32(0xFF, g, 0, 0xFF);
}
draw_list.AddRectFilled({cur_pos_x - width, final_pos_y - height}, {cur_pos_x, final_pos_y},
color);
cur_pos_x -= width;
if (cur_pos_x < width) {
break;
}
}
draw_list.PopClipRect();
}
} // namespace FrameGraph
static void DrawSimple() { static void DrawSimple() {
const auto io = GetIO(); const auto io = GetIO();
Text("FPS: %.1f (%.3f ms)", io.Framerate, 1000.0f / io.Framerate); Text("FPS: %.1f (%.3f ms)", io.Framerate, 1000.0f / io.Framerate);
} }
static void DrawAdvanced() { static void DrawAdvanced() {
const auto& ctx = *GetCurrentContext(); const auto& ctx = *GImGui;
const auto& io = ctx.IO; const auto& io = ctx.IO;
const auto& window = *ctx.CurrentWindow; const auto& window = *ctx.CurrentWindow;
auto& draw_list = *window.DrawList; auto& draw_list = *window.DrawList;
Text("Frame time: %.3f ms (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); auto isSystemPaused = sysState->IsGuestThreadsPaused();
static float deltaTime;
static float frameRate;
if (!isSystemPaused) {
deltaTime = io.DeltaTime * 1000.0f;
frameRate = io.Framerate;
}
Text("Frame time: %.3f ms (%.1f FPS)", deltaTime, frameRate);
SeparatorText("Frame graph"); SeparatorText("Frame graph");
const float full_width = GetContentRegionAvail().x; FrameGraph::Draw();
{ // Frame graph - inspired by SeparatorText("System debug");
// https://asawicki.info/news_1758_an_idea_for_visualization_of_frame_times BeginDisabled(isSystemPaused);
auto pos = GetCursorScreenPos(); if (Button("Pause")) {
const ImVec2 size{full_width, frame_graph_height + FRAME_GRAPH_PADDING_Y * 2.0f}; sysState->PauseGuestThreads();
ItemSize(size);
if (!ItemAdd({pos, pos + size}, GetID("FrameGraph"))) {
return;
}
float target_dt = 1.0f / (TARGET_FPS * (float)Config::vblankDiv());
float cur_pos_x = pos.x + full_width;
pos.y += FRAME_GRAPH_PADDING_Y;
const float final_pos_y = pos.y + frame_graph_height;
draw_list.AddRectFilled({pos.x, pos.y - FRAME_GRAPH_PADDING_Y},
{pos.x + full_width, final_pos_y + FRAME_GRAPH_PADDING_Y},
IM_COL32(0x33, 0x33, 0x33, 0xFF));
draw_list.PushClipRect({pos.x, pos.y}, {pos.x + full_width, final_pos_y}, true);
for (u32 i = 0; i < FRAME_BUFFER_SIZE; ++i) {
const auto& frame_info = frame_list[(current_frame - i) % FRAME_BUFFER_SIZE];
const float dt_factor = target_dt / frame_info.delta;
const float width = std::ceil(BAR_WIDTH_MULT / dt_factor);
const float height =
std::min(std::log2(BAR_HEIGHT_MULT / dt_factor) / 3.0f, 1.0f) * frame_graph_height;
ImU32 color;
if (dt_factor >= 0.95f) { // BLUE
color = IM_COL32(0x33, 0x33, 0xFF, 0xFF);
} else if (dt_factor >= 0.5f) { // GREEN <> YELLOW
float t = 1.0f - (dt_factor - 0.5f) * 2.0f;
int r = (int)(0xFF * t);
color = IM_COL32(r, 0xFF, 0, 0xFF);
} else { // YELLOW <> RED
float t = dt_factor * 2.0f;
int g = (int)(0xFF * t);
color = IM_COL32(0xFF, g, 0, 0xFF);
}
draw_list.AddRectFilled({cur_pos_x - width, final_pos_y - height},
{cur_pos_x, final_pos_y}, color);
cur_pos_x -= width;
if (cur_pos_x < width) {
break;
}
}
draw_list.PopClipRect();
} }
EndDisabled();
SameLine();
BeginDisabled(!isSystemPaused);
if (Button("Resume")) {
sysState->ResumeGuestThreads();
}
EndDisabled();
} }
void Layers::VideoInfo::Draw() { void Layers::VideoInfo::Draw() {
const auto io = GetIO(); const auto io = GetIO();
const FrameInfo frame_info{ if (!sysState->IsGuestThreadsPaused()) {
.num = ++current_frame, const FrameInfo frame_info{
.delta = io.DeltaTime, .num = ++current_frame,
}; .delta = io.DeltaTime,
frame_list[current_frame % FRAME_BUFFER_SIZE] = frame_info; };
FrameGraph::frame_list[current_frame % FrameGraph::FRAME_BUFFER_SIZE] = frame_info;
}
if (IsKeyPressed(ImGuiKey_F10, false)) { if (IsKeyPressed(ImGuiKey_F10, false)) {
const bool changed_ctrl = io.KeyCtrl != show_advanced; const bool changed_ctrl = io.KeyCtrl != show_advanced;
@ -108,11 +147,13 @@ void Layers::VideoInfo::Draw() {
if (show) { if (show) {
if (show_advanced) { if (show_advanced) {
if (Begin("Video Debug Info", nullptr, ImGuiWindowFlags_NoDecoration)) { if (Begin("Video Debug Info", &show, 0)) {
DrawAdvanced(); DrawAdvanced();
} }
} else { } else {
if (Begin("Video Info", nullptr, ImGuiWindowFlags_NoDecoration)) { if (Begin("Video Info", nullptr,
ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_AlwaysAutoResize)) {
DrawSimple(); DrawSimple();
} }
} }