Files
ReSkate/Runtime/frame_timing.cpp
T
AyeZeeBB 4ac1343b95 Crash reports: self-hosted Sentry instead of Backtrace, and opt-in performance timings
Crash dumps now go to the project's own Sentry server. The launcher's
out-of-process helper is unchanged; only where it posts and what it expects
back are:

- The default endpoint is the Sentry project's minidump URL. Report
  attributes travel as Sentry tags, with the release as reskate@<version>.
- An upload counts as accepted when the server answers with an event ID.
- "backtrace" is "crash_report" throughout: the namespace, files, CMake
  targets, DINGOSDK_CRASH_REPORT_URL, DINGOSDK_BUILD_CRASH_REPORT_TESTS and
  the RESKATE_CRASH_REPORT_URL override.

Performance timings are new, and off unless "Send performance timings" is
ticked in the launcher's settings (RESKATE_TRACING=1). A small tracer
(Engine/Core/Debug/trace.h) posts each finished step to Sentry as a
transaction from a background thread; the launcher and the game it starts
share one trace. Timed: the update check, the Steam download, Thunderstore
installs, the mod merge, starting the game, the launch up to the first
playable level, each level load, joining a session, fetching the identity
lists, and the time spent in each level with its average frame rate and
long frames. Turning crash reports off turns timings off too.
2026-10-12 23:24:33 -05:00

38 lines
1.9 KiB
C++

#include "runtime_internal.h"
#include "Engine/Core/Debug/trace.h"
#include "Engine/Core/Hooks/hooks.h"
#include "Engine/Core/Profiling/profiler.h"
#include "Engine/Game/Build/addresses.h"
#include "Engine/Game/Build/20260929/profiler_labels.h"
// The game's client frame job, timed for the profiler (Engine/Core/Profiling/profiler.h). It runs
// the whole client frame, ReSkate's tick included, so its time against the sim budget (16.7 ms at
// 60 Hz) says how close the game is to slow motion. Two clock reads per client frame.
namespace dingosdk::runtime::detail {
namespace {
using ClientFrameJob = std::uintptr_t (*)(std::uintptr_t, std::uintptr_t, std::uintptr_t, std::uintptr_t);
ClientFrameJob original_client_frame_job{};
std::uintptr_t client_frame_job(std::uintptr_t job, std::uintptr_t timing, std::uintptr_t dependencies, std::uintptr_t extra) {
const auto start = dingosdk::profiler::now_ns();
dingosdk::trace::frame(start); // frame pacing for a level session's timing; a few atomic adds
const auto result = original_client_frame_job(job, timing, dependencies, extra);
dingosdk::profiler::record_client_frame(dingosdk::profiler::now_ns() - start);
return result;
}
}
void start_frame_timing() {
const auto& contract = addr::profiler_labels::client_frame_job;
auto* target = reinterpret_cast<void*>(runtime().base + contract.rva);
bool enabled = false;
if (matches(reinterpret_cast<std::uintptr_t>(target), contract.bytes)) {
void* original{};
if (dingosdk::hook_prepare(target, reinterpret_cast<void*>(&client_frame_job), &original) == dingosdk::HookOk) {
original_client_frame_job = reinterpret_cast<ClientFrameJob>(original);
enabled = dingosdk::hook_enable(target) == dingosdk::HookOk;
if (!enabled) (void)dingosdk::hook_remove(target);
}
}
record(std::string("{\"event\":\"frame_timing_initialized\",\"active\":") + (enabled ? "true}" : "false}"));
}
}