pull/3797/merge
jpchow26 1 week ago committed by GitHub
commit 6623294d31
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -2795,6 +2795,10 @@ void FullscreenUI::DrawInterfaceSettingsPage()
DrawToggleSetting(bsi, FSUI_ICONVSTR(ICON_FA_CHART_BAR, "Show GPU Statistics"),
FSUI_VSTR("Shows information about the emulated GPU in the top-right corner of the display."),
"Display", "ShowGPUStatistics", false);
DrawToggleSetting(bsi, FSUI_ICONVSTR(ICON_FA_CHART_BAR, "Show Bandwidth Statistics"),
FSUI_VSTR("Shows VRAM memory bandwidth (writes, readbacks, copies, display) per frame in the "
"top-right corner of the display."),
"Display", "ShowBandwidthStatistics", false);
DrawToggleSetting(
bsi, FSUI_ICONVSTR(ICON_FA_USER_CLOCK, "Show Latency Statistics"),
FSUI_VSTR("Shows information about input and audio latency in the top-right corner of the display."), "Display",

@ -117,7 +117,8 @@ bool GPUBackend::Initialize(bool clear_vram, Error* error)
bool GPUBackend::UpdateSettings(const GPUSettings& old_settings, Error* error)
{
if (g_gpu_settings.display_show_gpu_stats != old_settings.display_show_gpu_stats)
if (g_gpu_settings.display_show_gpu_stats != old_settings.display_show_gpu_stats ||
g_gpu_settings.display_show_bandwidth_stats != old_settings.display_show_bandwidth_stats)
GPUBackend::ResetStatistics();
return true;
@ -654,6 +655,31 @@ void GPUBackend::GetMemoryStatsString(SmallStringBase& str) const
s_stats.host_num_copies, s_stats.host_num_uploads);
}
void GPUBackend::GetBandwidthStatsString(SmallStringBase& str) const
{
// VRAM memory bandwidth in KB per frame, by category. "BW" is the total;
// W = CPU->GPU writes, R = GPU->CPU readbacks, C = GPU->GPU copies, D = display extraction.
const u32 total_kb = static_cast<u32>((s_stats.vram_bandwidth.total() + (1024 - 1)) / 1024);
const u32 write_kb = static_cast<u32>((s_stats.vram_bandwidth.write + (1024 - 1)) / 1024);
const u32 read_kb = static_cast<u32>((s_stats.vram_bandwidth.read + (1024 - 1)) / 1024);
const u32 copy_kb = static_cast<u32>((s_stats.vram_bandwidth.copy + (1024 - 1)) / 1024);
const u32 display_kb = static_cast<u32>((s_stats.vram_bandwidth.display + (1024 - 1)) / 1024);
str.format("{}KB " BOLD("BW") " | {} " BOLD("W") " | {} " BOLD("R") " | {} " BOLD("C") " | {} " BOLD("D"),
total_kb, write_kb, read_kb, copy_kb, display_kb);
}
void GPUBackend::GetRenderStatsString(SmallStringBase& str) const
{
// Rendering / fill-rate in thousands of shaded pixels per frame. "FP" is the total
// shaded-pixel count (bounding-box estimate, includes overdraw); "TX" is the subset
// that samples a texture (i.e. the texture-fetch cost).
const u32 fill_k = static_cast<u32>((s_stats.render.fill + 1000 - 1) / 1000);
const u32 tex_k = static_cast<u32>((s_stats.render.tex + 1000 - 1) / 1000);
str.format("{}K " BOLD("FP") " | {}K " BOLD("TX"), fill_k, tex_k);
}
#undef BOLD
void GPUBackend::ResetStatistics()
@ -688,6 +714,16 @@ void GPUBackend::UpdateStatistics(u32 frame_count)
UPDATE_GPU_STAT(num_downloads);
UPDATE_GPU_STAT(num_uploads);
// VRAM memory bandwidth (per-frame average).
s_stats.vram_bandwidth.write = (s_counters.vram_write_bytes + round) / frame_count;
s_stats.vram_bandwidth.read = (s_counters.vram_read_bytes + round) / frame_count;
s_stats.vram_bandwidth.copy = (s_counters.vram_copy_bytes + round) / frame_count;
s_stats.vram_bandwidth.display = (s_counters.vram_display_bytes + round) / frame_count;
// Rendering / fill-rate (per-frame average).
s_stats.render.fill = (s_counters.fill_pixels + round) / frame_count;
s_stats.render.tex = (s_counters.tex_pixels + round) / frame_count;
#undef UPDATE_GPU_STAT
#undef UPDATE_COUNTER

@ -92,6 +92,8 @@ public:
void GetStatsString(SmallStringBase& str) const;
void GetMemoryStatsString(SmallStringBase& str) const;
void GetBandwidthStatsString(SmallStringBase& str) const;
void GetRenderStatsString(SmallStringBase& str) const;
void ResetStatistics();
void UpdateStatistics(u32 frame_count);
@ -130,6 +132,20 @@ protected:
u32 num_vertices;
u32 num_primitives;
u32 num_depth_buffer_clears;
// VRAM memory bandwidth in bytes (hardware backend only). These measure the actual
// memory-bus traffic of VRAM operations, which is the bottleneck on low-bandwidth
// SoCs (e.g. Allwinner H700 / panfrost).
size_t vram_write_bytes; // CPU->GPU uploads + framebuffer writes (+ reads when checking mask)
size_t vram_read_bytes; // GPU->CPU readbacks
size_t vram_copy_bytes; // GPU->GPU VRAM copies (read + write)
size_t vram_display_bytes; // display extraction (read + write)
// Rendering / fill-rate in shaded pixels (hardware backend only). These measure the
// GPU rasterization cost, which (unlike VRAM bandwidth) scales with what is actually
// on screen - the likely bottleneck for animated-texture scenes on weak GPUs.
size_t fill_pixels; // total shaded pixels (bounding-box estimate, includes overdraw)
size_t tex_pixels; // shaded pixels that sample a texture (texture-fetch cost)
};
struct Stats : Counters
@ -143,6 +159,26 @@ protected:
u32 host_num_uploads;
u8 gpu_busy_pct;
/// Per-frame average VRAM memory bandwidth in bytes, by category.
struct Bandwidth
{
size_t write;
size_t read;
size_t copy;
size_t display;
ALWAYS_INLINE size_t total() const { return (write + read + copy + display); }
};
Bandwidth vram_bandwidth;
/// Per-frame average rendering / fill-rate in shaded pixels.
struct Render
{
size_t fill;
size_t tex;
};
Render render;
};
virtual void ReadVRAM(u32 x, u32 y, u32 width, u32 height) = 0;

@ -3007,6 +3007,8 @@ void GPU_HW::DrawSprite(const GPUBackendDrawRectangleCommand* cmd)
return;
}
AccountRenderPixels(clamped_rect, cmd->texture_enable);
// Treat non-textured sprite draws as fills, so we don't break the TC on framebuffer clears.
bool draw_with_software_renderer = m_draw_with_software_renderer;
if (m_use_texture_cache && !cmd->transparency_enable && !cmd->shading_enable && !cmd->texture_enable &&
@ -3139,6 +3141,11 @@ void GPU_HW::DrawPolygon(const GPUBackendDrawPolygonCommand* cmd)
{
SetBatchDepthBuffer(cmd, false);
if (!clamped_draw_rect_012.rempty())
AccountRenderPixels(clamped_draw_rect_012, cmd->texture_enable);
if (!clamped_draw_rect_123.rempty())
AccountRenderPixels(clamped_draw_rect_123, cmd->texture_enable);
FinishPolygonDraw(cmd, vertices, num_vertices, false, false, clamped_draw_rect_012, clamped_draw_rect_123);
}
@ -3186,6 +3193,11 @@ void GPU_HW::DrawPrecisePolygon(const GPUBackendDrawPrecisePolygonCommand* cmd)
CheckForDepthClear(cmd, average_z);
}
if (!clamped_draw_rect_012.rempty())
AccountRenderPixels(clamped_draw_rect_012, cmd->texture_enable);
if (!clamped_draw_rect_123.rempty())
AccountRenderPixels(clamped_draw_rect_123, cmd->texture_enable);
FinishPolygonDraw(cmd, vertices, num_vertices, true, is_3d, clamped_draw_rect_012, clamped_draw_rect_123);
}
@ -3719,6 +3731,9 @@ void GPU_HW::DownloadVRAMFromGPU(u32 x, u32 y, u32 width, u32 height)
VRAM_WIDTH * sizeof(u16));
}
// VRAM bandwidth: framebuffer read (4 bytes/pixel) + CPU readback copy (2 bytes/pixel).
GPUBackend::s_counters.vram_read_bytes += static_cast<size_t>(copy_rect.width()) * copy_rect.height() * 6;
RestoreDeviceContext();
}
@ -3824,6 +3839,11 @@ void GPU_HW::UpdateVRAMOnGPU(u32 x, u32 y, u32 width, u32 height, const void* da
DrawScreenQuad(scaled_bounds, m_vram_texture->GetSizeVec(), GSVector4::zero(), &uniforms, sizeof(uniforms));
// VRAM bandwidth: upload (2 bytes/pixel) + framebuffer write (4 bytes/pixel) +
// framebuffer read for the mask check (4 bytes/pixel).
GPUBackend::s_counters.vram_write_bytes +=
static_cast<size_t>(width) * height * (2 + 4 + (check_mask ? 4 : 0));
RestoreDeviceContext();
}
@ -3880,6 +3900,10 @@ void GPU_HW::CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32
return;
}
// VRAM bandwidth: a copy reads and writes the framebuffer (4 bytes/pixel each).
// (The recursive chunk path above counts itself; the local-memory TC path is CPU-side.)
GPUBackend::s_counters.vram_copy_bytes += static_cast<size_t>(width) * height * 8;
if (use_shader || IsUsingMultisampling())
{
if (intersect_with_draw || intersect_with_write)
@ -4375,6 +4399,11 @@ void GPU_HW::UpdateDisplay(const GPUBackendUpdateDisplayCommand* cmd)
static_cast<float>(line_skip ? 2 : 1)};
g_gpu_device->DrawWithPushConstants(3, 0, &uniforms, sizeof(uniforms));
// VRAM bandwidth: the display extraction reads the VRAM framebuffer and writes the
// extract texture (4 bytes/pixel each, at the scaled resolution).
GPUBackend::s_counters.vram_display_bytes +=
static_cast<size_t>(scaled_display_width) * scaled_display_height * 8;
m_vram_extract_texture->MakeReadyForSampling();
if (depth_source)
{

@ -99,11 +99,20 @@ public:
void UpdateDisplay(const GPUBackendUpdateDisplayCommand* cmd) override;
private:
/// Accounts for the shaded-pixel (fill-rate) cost of a drawn rectangle.
ALWAYS_INLINE void AccountRenderPixels(const GSVector4i& rect, bool textured)
{
const size_t pixels = static_cast<size_t>(rect.width()) * rect.height();
s_counters.fill_pixels += pixels;
if (textured)
s_counters.tex_pixels += pixels;
}
enum : u32
{
MAX_BATCH_VERTEX_COUNTER_IDS = 65536 - 2,
MAX_VERTICES_FOR_RECTANGLE = 6 * (((MAX_PRIMITIVE_WIDTH + (TEXTURE_PAGE_WIDTH - 1)) / TEXTURE_PAGE_WIDTH) + 1u) *
(((MAX_PRIMITIVE_HEIGHT + (TEXTURE_PAGE_HEIGHT - 1)) / TEXTURE_PAGE_HEIGHT) + 1u),
(((MAX_PRIMITIVE_HEIGHT + (TEXTURE_PAGE_HEIGHT - 1)) / TEXTURE_PAGE_HEIGHT) + 1u),
NUM_TEXTURE_MODES = static_cast<u32>(BatchTextureMode::MaxCount),
};
enum : u8

@ -389,9 +389,9 @@ void ImGuiManager::DrawPerformanceOverlay(const GPUBackend* gpu, float& position
#define COLOR(text) "\x04" text "\x03"
if (!(g_gpu_settings.display_show_fps || g_gpu_settings.display_show_speed || g_gpu_settings.display_show_gpu_stats ||
g_gpu_settings.display_show_resolution || g_gpu_settings.display_show_latency_stats ||
g_gpu_settings.display_show_cpu_usage || g_gpu_settings.display_show_gpu_usage ||
g_gpu_settings.display_show_frame_times ||
g_gpu_settings.display_show_bandwidth_stats || g_gpu_settings.display_show_resolution ||
g_gpu_settings.display_show_latency_stats || g_gpu_settings.display_show_cpu_usage ||
g_gpu_settings.display_show_gpu_usage || g_gpu_settings.display_show_frame_times ||
(g_gpu_settings.display_show_status_indicators &&
(VideoThread::IsSystemPaused() || System::IsFastForwardEnabled() || System::IsTurboEnabled()))))
{
@ -456,6 +456,17 @@ void ImGuiManager::DrawPerformanceOverlay(const GPUBackend* gpu, float& position
position_y += spacing;
}
if (g_gpu_settings.display_show_bandwidth_stats)
{
gpu->GetBandwidthStatsString(text);
DrawPerformanceStat(dl, position_y, fixed_font, fixed_font_size, FIXED_BOLD_WEIGHT, 0, rbound, text);
position_y += spacing;
gpu->GetRenderStatsString(text);
DrawPerformanceStat(dl, position_y, fixed_font, fixed_font_size, FIXED_BOLD_WEIGHT, 0, rbound, text);
position_y += spacing;
}
if (g_gpu_settings.display_show_resolution)
{
const u32 resolution_scale = gpu->GetResolutionScale();

@ -429,6 +429,7 @@ void Settings::Load(const SettingsInterface& si, const SettingsInterface& contro
display_show_fps = si.GetBoolValue("Display", "ShowFPS", false);
display_show_speed = si.GetBoolValue("Display", "ShowSpeed", false);
display_show_gpu_stats = si.GetBoolValue("Display", "ShowGPUStatistics", false);
display_show_bandwidth_stats = si.GetBoolValue("Display", "ShowBandwidthStatistics", false);
display_show_resolution = si.GetBoolValue("Display", "ShowResolution", false);
display_show_latency_stats = si.GetBoolValue("Display", "ShowLatencyStatistics", false);
display_show_cpu_usage = si.GetBoolValue("Display", "ShowCPU", false);
@ -806,6 +807,7 @@ void Settings::Save(SettingsInterface& si, bool for_copy) const
si.SetBoolValue("Display", "ShowResolution", display_show_resolution);
si.SetBoolValue("Display", "ShowLatencyStatistics", display_show_latency_stats);
si.SetBoolValue("Display", "ShowGPUStatistics", display_show_gpu_stats);
si.SetBoolValue("Display", "ShowBandwidthStatistics", display_show_bandwidth_stats);
si.SetBoolValue("Display", "ShowCPU", display_show_cpu_usage);
si.SetBoolValue("Display", "ShowGPU", display_show_gpu_usage);
si.SetBoolValue("Display", "ShowFrameTimes", display_show_frame_times);

@ -111,6 +111,7 @@ struct GPUSettings
bool display_show_fps : 1 = false;
bool display_show_speed : 1 = false;
bool display_show_gpu_stats : 1 = false;
bool display_show_bandwidth_stats : 1 = false;
bool display_show_resolution : 1 = false;
bool display_show_latency_stats : 1 = false;
bool display_show_cpu_usage : 1 = false;

@ -72,6 +72,8 @@ OSDSettingsWidget::OSDSettingsWidget(SettingsWindow* dialog, QWidget* parent) :
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showGPU, "Display", "ShowGPU", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showInput, "Display", "ShowInputs", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showGPUStatistics, "Display", "ShowGPUStatistics", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showBandwidthStatistics, "Display", "ShowBandwidthStatistics",
false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showLatencyStatistics, "Display", "ShowLatencyStatistics",
false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showFrameTimes, "Display", "ShowFrameTimes", false);

@ -331,6 +331,13 @@
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QCheckBox" name="showBandwidthStatistics">
<property name="text">
<string>Show Bandwidth Statistics</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>

Loading…
Cancel
Save