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/signals.cpp
src/core/signals.h
src/core/system.cpp
src/core/system.h
src/core/tls.cpp
src/core/tls.h
src/core/virtual_memory.cpp

View file

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

View file

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

View file

@ -7,6 +7,10 @@
#include "common/types.h"
namespace Common {
class NativeClock;
}
namespace Core::Loader {
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_RAW_NETWORK = 19;
namespace Dev {
u64& GetInitialPtc();
Common::NativeClock* GetClock();
} // namespace Dev
u64 PS4_SYSV_ABI sceKernelGetTscFrequency();
u64 PS4_SYSV_ABI sceKernelGetProcessTime();
u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounter();

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <thread>
#include <utility>
#include <imgui.h>
#include "common/assert.h"
@ -281,10 +282,15 @@ void MsgDialogUi::Draw() {
first_render = false;
}
DialogResult Libraries::MsgDialog::ShowMsgDialog(MsgDialogState state, bool block) {
DialogResult result{};
Status status = Status::RUNNING;
MsgDialogUi dialog(&state, &status, &result);
DialogResult Libraries::MsgDialog::ShowMsgDialog(MsgDialogState p_state, bool block) {
static DialogResult result{};
static Status status;
static MsgDialogUi dialog;
static MsgDialogState state;
dialog = MsgDialogUi{};
status = Status::RUNNING;
state = std::move(p_state);
dialog = MsgDialogUi(&state, &status, &result);
if (block) {
while (status == Status::RUNNING) {
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/videoout/driver.h"
#include "core/platform.h"
#include "core/system.h"
#include "video_core/renderer_vulkan/renderer_vulkan.h"
extern std::unique_ptr<Vulkan::RendererVulkan> renderer;
@ -265,6 +266,8 @@ void VideoOutDriver::PresentThread(std::stop_token token) {
Common::AccurateTimer timer{vblank_period};
auto systemState = Common::Singleton<SystemState>::Instance();
const auto receive_request = [this] -> Request {
std::scoped_lock lk{mutex};
if (!requests.empty()) {
@ -284,7 +287,7 @@ void VideoOutDriver::PresentThread(std::stop_token token) {
if (vblank_status.count % (main_port.flip_rate + 1) == 0) {
const auto request = receive_request();
if (!request) {
if (!main_port.is_open) {
if (!main_port.is_open || systemState->IsGuestThreadsPaused()) {
DrawBlankFrame();
}
} else {

View file

@ -18,6 +18,7 @@
#include "core/memory.h"
#include "core/tls.h"
#include "core/virtual_memory.h"
#include "system.h"
namespace Core {
@ -88,6 +89,7 @@ void Linker::Execute() {
// Init primary thread.
Common::SetCurrentThreadName("GAME_MainThread");
Common::Singleton<SystemState>::Instance()->AddCurrentThreadToGuestList();
Libraries::Kernel::pthreadInitSelfMainThread();
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));
}
break;
case SIGUSR1: { // Sleep thread until signal is received
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGUSR1);
sigwait(&sigset, &sig);
} break;
default:
break;
}
@ -105,6 +111,8 @@ SignalDispatch::SignalDispatch() {
"Failed to register access violation signal handler.");
ASSERT_MSG(sigaction(SIGILL, &action, nullptr) == 0,
"Failed to register illegal instruction signal handler.");
ASSERT_MSG(sigaction(SIGUSR1, &action, nullptr) == 0,
"Failed to register sleep signal handler.");
#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 "common/config.h"
#include "common/singleton.h"
#include "common/types.h"
#include "core/system.h"
#include "imgui_internal.h"
#include "video_info.h"
@ -18,7 +20,11 @@ struct FrameInfo {
static bool show = false;
static bool show_advanced = false;
static auto sysState = Common::Singleton<SystemState>::Instance();
static u32 current_frame = 0;
namespace FrameGraph {
constexpr float TARGET_FPS = 60.0f;
constexpr u32 FRAME_BUFFER_SIZE = 1024;
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 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() {
const auto io = GetIO();
Text("FPS: %.1f (%.3f ms)", io.Framerate, 1000.0f / io.Framerate);
}
static void DrawAdvanced() {
const auto& ctx = *GetCurrentContext();
const auto& ctx = *GImGui;
const auto& io = ctx.IO;
const auto& window = *ctx.CurrentWindow;
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");
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();
FrameGraph::Draw();
SeparatorText("System debug");
BeginDisabled(isSystemPaused);
if (Button("Pause")) {
sysState->PauseGuestThreads();
}
EndDisabled();
SameLine();
BeginDisabled(!isSystemPaused);
if (Button("Resume")) {
sysState->ResumeGuestThreads();
}
EndDisabled();
}
void Layers::VideoInfo::Draw() {
const auto io = GetIO();
const FrameInfo frame_info{
.num = ++current_frame,
.delta = io.DeltaTime,
};
frame_list[current_frame % FRAME_BUFFER_SIZE] = frame_info;
if (!sysState->IsGuestThreadsPaused()) {
const FrameInfo frame_info{
.num = ++current_frame,
.delta = io.DeltaTime,
};
FrameGraph::frame_list[current_frame % FrameGraph::FRAME_BUFFER_SIZE] = frame_info;
}
if (IsKeyPressed(ImGuiKey_F10, false)) {
const bool changed_ctrl = io.KeyCtrl != show_advanced;
@ -108,11 +147,13 @@ void Layers::VideoInfo::Draw() {
if (show) {
if (show_advanced) {
if (Begin("Video Debug Info", nullptr, ImGuiWindowFlags_NoDecoration)) {
if (Begin("Video Debug Info", &show, 0)) {
DrawAdvanced();
}
} else {
if (Begin("Video Info", nullptr, ImGuiWindowFlags_NoDecoration)) {
if (Begin("Video Info", nullptr,
ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_AlwaysAutoResize)) {
DrawSimple();
}
}