mirror of
https://github.com/ARMSX2/ARMSX2.git
synced 2026-08-24 16:50:16 -07:00
GS tooling: report GPU device and driver identity in stats output
Both `gsctl stats` and `gsrunner -stats-json` reported counters with no indication of which GPU or driver produced them, so a stats blob from a tester was not attributable — and driver identity is the axis nearly every mobile GPU behaviour turns on. This matters beyond provenance. Several GS features are force-overridden per-driver (framebuffer fetch and texture barriers are forced on for Adreno regardless of INI), so without knowing the driver you cannot tell whether a settings A/B was applied at all or silently compared a config against itself. gsrunner captures the strings on the GS thread at first present rather than at shutdown, where the device may already be gone. Both emitters escape the values, since GetDriverInfo() is multi-line on Vulkan.
This commit is contained in:
@@ -148,6 +148,8 @@ static std::deque<std::function<void()>> s_cpu_thread_tasks;
|
||||
static std::string s_stats_json_path;
|
||||
static std::string s_drawlog_path;
|
||||
static std::vector<FrameSample> s_frame_samples;
|
||||
static std::string s_device_name;
|
||||
static std::string s_driver_info;
|
||||
static u64 s_frame_timer_last = 0;
|
||||
static double s_last_prims = 0;
|
||||
static double s_last_tc_source_hit = 0;
|
||||
@@ -339,6 +341,15 @@ void Host::BeginPresentFrame()
|
||||
|
||||
if (GSIsHardwareRenderer())
|
||||
{
|
||||
// Captured here rather than at shutdown: this runs on the GS thread with the
|
||||
// device definitely live, and it is the axis that decides whether a settings
|
||||
// A/B was even applied (several GS features are force-overridden per-driver).
|
||||
if (s_device_name.empty() && g_gs_device)
|
||||
{
|
||||
s_device_name = g_gs_device->GetName();
|
||||
s_driver_info = g_gs_device->GetDriverInfo();
|
||||
}
|
||||
|
||||
const u32 last_draws = s_total_internal_draws;
|
||||
const u32 last_uploads = s_total_uploads;
|
||||
|
||||
@@ -1161,7 +1172,25 @@ static void WriteStatsJson(const std::string& path)
|
||||
}
|
||||
}
|
||||
|
||||
// GetDriverInfo() is multi-line on Vulkan, and neither string is JSON-safe as-is.
|
||||
const auto json_escape = [](const std::string& in) {
|
||||
std::string out;
|
||||
out.reserve(in.size());
|
||||
for (const char c : in)
|
||||
{
|
||||
if (c == '\n' || c == '\r' || c == '\t')
|
||||
out.push_back(' ');
|
||||
else if (c == '"' || c == '\\')
|
||||
out.push_back('\'');
|
||||
else
|
||||
out.push_back(c);
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
std::fprintf(fp.get(), "{\n \"run\": {\n");
|
||||
std::fprintf(fp.get(), " \"device_name\": \"%s\",\n \"driver_info\": \"%s\",\n",
|
||||
json_escape(s_device_name).c_str(), json_escape(s_driver_info).c_str());
|
||||
std::fprintf(fp.get(), " \"frames\": %u,\n \"drawn_frames\": %u,\n", s_total_frames, s_total_drawn_frames);
|
||||
std::fprintf(fp.get(), " \"prims\": %" PRIu64 ",\n \"draws\": %" PRIu64 ",\n \"draw_calls\": %" PRIu64 ",\n",
|
||||
s_total_prims, s_total_internal_draws, s_total_draws);
|
||||
|
||||
+30
-3
@@ -7,6 +7,7 @@
|
||||
#include "Elfheader.h"
|
||||
#include "GS.h"
|
||||
#include "GS/GSPerfMon.h"
|
||||
#include "GS/Renderers/Common/GSDevice.h"
|
||||
#include "MTGS.h"
|
||||
#include "PerformanceMetrics.h"
|
||||
#include "SaveState.h"
|
||||
@@ -306,11 +307,35 @@ namespace PINEServer
|
||||
static std::string BuildStatsJson()
|
||||
{
|
||||
SmallString gs_memory;
|
||||
// Device identity is the axis nearly every mobile GPU bug turns on, so a stats
|
||||
// blob without it is not attributable to a driver. Adreno tiers run drivers
|
||||
// (Mesa Turnip vs Qualcomm proprietary) that behave oppositely for fbfetch and
|
||||
// push descriptors, so the driver string matters more than the device name.
|
||||
std::string device_name, driver_info;
|
||||
if (MTGS::IsOpen())
|
||||
{
|
||||
MTGS::RunOnGSThread([&gs_memory]() { GSgetMemoryStats(gs_memory); });
|
||||
MTGS::RunOnGSThread([&gs_memory, &device_name, &driver_info]() {
|
||||
GSgetMemoryStats(gs_memory);
|
||||
if (g_gs_device)
|
||||
{
|
||||
device_name = g_gs_device->GetName();
|
||||
driver_info = g_gs_device->GetDriverInfo();
|
||||
}
|
||||
});
|
||||
MTGS::WaitGS(false);
|
||||
}
|
||||
// Newlines and quotes would break the JSON; GetDriverInfo() is multi-line on Vulkan.
|
||||
const auto sanitize = [](std::string& s) {
|
||||
for (char& c : s)
|
||||
{
|
||||
if (c == '\n' || c == '\r' || c == '\t')
|
||||
c = ' ';
|
||||
else if (c == '"' || c == '\\')
|
||||
c = '\'';
|
||||
}
|
||||
};
|
||||
sanitize(device_name);
|
||||
sanitize(driver_info);
|
||||
|
||||
const auto counter = [](GSPerfMon::counter_t c) { return g_perfmon.Get(c); };
|
||||
|
||||
@@ -330,7 +355,8 @@ namespace PINEServer
|
||||
"\"tc_source_hit\":{:.1f},\"tc_source_miss\":{:.1f},"
|
||||
"\"tc_target_hit\":{:.1f},\"tc_target_miss\":{:.1f},"
|
||||
"\"hash_cache_hit\":{:.1f},\"hash_cache_miss\":{:.1f},"
|
||||
"\"gs_memory\":\"{}\",\"frame_number\":{}"
|
||||
"\"gs_memory\":\"{}\",\"frame_number\":{},"
|
||||
"\"renderer\":\"{}\",\"device_name\":\"{}\",\"driver_info\":\"{}\""
|
||||
"}}",
|
||||
PerformanceMetrics::GetFPS(), PerformanceMetrics::GetInternalFPS(), PerformanceMetrics::GetSpeed(),
|
||||
PerformanceMetrics::GetAverageFrameTime(), PerformanceMetrics::GetMinimumFrameTime(),
|
||||
@@ -348,7 +374,8 @@ namespace PINEServer
|
||||
counter(GSPerfMon::TCSourceHit), counter(GSPerfMon::TCSourceMiss),
|
||||
counter(GSPerfMon::TCTargetHit), counter(GSPerfMon::TCTargetMiss),
|
||||
counter(GSPerfMon::HashCacheHit), counter(GSPerfMon::HashCacheMiss),
|
||||
gs_memory.view(), PerformanceMetrics::GetFrameNumber());
|
||||
gs_memory.view(), PerformanceMetrics::GetFrameNumber(),
|
||||
Pcsx2Config::GSOptions::GetRendererName(EmuConfig.GS.Renderer), device_name, driver_info);
|
||||
}
|
||||
} // namespace PINEServer
|
||||
|
||||
|
||||
Reference in New Issue
Block a user