GPU: Add host/hardware stats

This commit is contained in:
Stenzek 2024-01-21 19:37:29 +10:00
parent 884c851079
commit 150ab8f4af
No known key found for this signature in database
24 changed files with 350 additions and 156 deletions

View file

@ -2778,6 +2778,9 @@ void FullscreenUI::DrawInterfaceSettingsPage()
FSUI_CSTR("Shows the number of frames (or v-syncs) displayed per second by the system in the top-right " FSUI_CSTR("Shows the number of frames (or v-syncs) displayed per second by the system in the top-right "
"corner of the display."), "corner of the display."),
"Display", "ShowFPS", false); "Display", "ShowFPS", false);
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_BARS, "Show GPU Statistics"),
FSUI_CSTR("Shows information about the emulated GPU in the top-right corner of the display."),
"Display", "ShowGPUStatistics", false);
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_BATTERY_HALF, "Show CPU Usage"), DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_BATTERY_HALF, "Show CPU Usage"),
FSUI_CSTR("Shows the host's CPU usage based on threads in the top-right corner of the display."), FSUI_CSTR("Shows the host's CPU usage based on threads in the top-right corner of the display."),
"Display", "ShowCPU", false); "Display", "ShowCPU", false);

View file

@ -21,6 +21,7 @@
#include "common/file_system.h" #include "common/file_system.h"
#include "common/heap_array.h" #include "common/heap_array.h"
#include "common/log.h" #include "common/log.h"
#include "common/small_string.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "stb_image_resize.h" #include "stb_image_resize.h"
@ -35,7 +36,10 @@ std::unique_ptr<GPU> g_gpu;
const GPU::GP0CommandHandlerTable GPU::s_GP0_command_handler_table = GPU::GenerateGP0CommandHandlerTable(); const GPU::GP0CommandHandlerTable GPU::s_GP0_command_handler_table = GPU::GenerateGP0CommandHandlerTable();
GPU::GPU() = default; GPU::GPU()
{
ResetStatistics();
}
GPU::~GPU() GPU::~GPU()
{ {
@ -66,7 +70,7 @@ bool GPU::Initialize()
return false; return false;
} }
g_gpu_device->SetGPUTimingEnabled(g_settings.display_show_gpu); g_gpu_device->SetGPUTimingEnabled(g_settings.display_show_gpu_usage);
return true; return true;
} }
@ -93,7 +97,7 @@ void GPU::UpdateSettings(const Settings& old_settings)
Panic("Failed to compile display pipeline on settings change."); Panic("Failed to compile display pipeline on settings change.");
} }
g_gpu_device->SetGPUTimingEnabled(g_settings.display_show_gpu); g_gpu_device->SetGPUTimingEnabled(g_settings.display_show_gpu_usage);
} }
void GPU::CPUClockChanged() void GPU::CPUClockChanged()
@ -2229,59 +2233,7 @@ void GPU::DrawDebugStateWindow()
return; return;
} }
const bool is_idle_frame = m_stats.num_polygons == 0; DrawRendererStats();
if (!is_idle_frame)
{
m_last_stats = m_stats;
m_stats = {};
}
if (ImGui::CollapsingHeader("Statistics", ImGuiTreeNodeFlags_DefaultOpen))
{
const Stats& stats = m_last_stats;
ImGui::Columns(2);
ImGui::SetColumnWidth(0, 200.0f * framebuffer_scale);
ImGui::TextUnformatted("Idle Frame: ");
ImGui::NextColumn();
ImGui::Text("%s", is_idle_frame ? "Yes" : "No");
ImGui::NextColumn();
ImGui::TextUnformatted("VRAM Reads: ");
ImGui::NextColumn();
ImGui::Text("%u", stats.num_vram_reads);
ImGui::NextColumn();
ImGui::TextUnformatted("VRAM Fills: ");
ImGui::NextColumn();
ImGui::Text("%u", stats.num_vram_fills);
ImGui::NextColumn();
ImGui::TextUnformatted("VRAM Writes: ");
ImGui::NextColumn();
ImGui::Text("%u", stats.num_vram_writes);
ImGui::NextColumn();
ImGui::TextUnformatted("VRAM Copies: ");
ImGui::NextColumn();
ImGui::Text("%u", stats.num_vram_copies);
ImGui::NextColumn();
ImGui::TextUnformatted("Vertices Processed: ");
ImGui::NextColumn();
ImGui::Text("%u", stats.num_vertices);
ImGui::NextColumn();
ImGui::TextUnformatted("Polygons Drawn: ");
ImGui::NextColumn();
ImGui::Text("%u", stats.num_polygons);
ImGui::NextColumn();
ImGui::Columns(1);
}
DrawRendererStats(is_idle_frame);
if (ImGui::CollapsingHeader("GPU", ImGuiTreeNodeFlags_DefaultOpen)) if (ImGui::CollapsingHeader("GPU", ImGuiTreeNodeFlags_DefaultOpen))
{ {
@ -2339,6 +2291,67 @@ void GPU::DrawDebugStateWindow()
ImGui::End(); ImGui::End();
} }
void GPU::DrawRendererStats(bool is_idle_frame) void GPU::DrawRendererStats()
{ {
} }
void GPU::GetStatsString(SmallStringBase& str)
{
if (IsHardwareRenderer())
{
str.format("{} HW | {} P | {} DC | {} RP | {} RB | {} C | {} W",
GPUDevice::RenderAPIToString(g_gpu_device->GetRenderAPI()), m_stats.num_primitives,
m_stats.host_num_draws, m_stats.host_num_render_passes, m_stats.num_reads, m_stats.num_copies,
m_stats.num_writes);
}
else
{
str.format("{} SW | {} P | {} R | {} C | {} W", GPUDevice::RenderAPIToString(g_gpu_device->GetRenderAPI()),
m_stats.num_primitives, m_stats.num_reads, m_stats.num_copies, m_stats.num_writes);
}
}
void GPU::GetMemoryStatsString(SmallStringBase& str)
{
const u32 vram_usage_mb = static_cast<u32>((g_gpu_device->GetVRAMUsage() + (1048576 - 1)) / 1048576);
const u32 stream_kb = static_cast<u32>((m_stats.host_buffer_streamed + (1024 - 1)) / 1024);
str.format("{} MB VRAM | {} KB STR | {} TC | {} TU", vram_usage_mb, stream_kb, m_stats.host_num_copies,
m_stats.host_num_uploads);
}
void GPU::ResetStatistics()
{
m_counters = {};
g_gpu_device->ResetStatistics();
}
void GPU::UpdateStatistics(u32 frame_count)
{
const GPUDevice::Statistics& stats = g_gpu_device->GetStatistics();
const u32 round = (frame_count - 1);
#define UPDATE_COUNTER(x) m_stats.x = (m_counters.x + round) / frame_count
#define UPDATE_GPU_STAT(x) m_stats.host_##x = (stats.x + round) / frame_count
UPDATE_COUNTER(num_reads);
UPDATE_COUNTER(num_writes);
UPDATE_COUNTER(num_copies);
UPDATE_COUNTER(num_vertices);
UPDATE_COUNTER(num_primitives);
// UPDATE_COUNTER(num_read_texture_updates);
// UPDATE_COUNTER(num_ubo_updates);
UPDATE_GPU_STAT(buffer_streamed);
UPDATE_GPU_STAT(num_draws);
UPDATE_GPU_STAT(num_render_passes);
UPDATE_GPU_STAT(num_copies);
UPDATE_GPU_STAT(num_downloads);
UPDATE_GPU_STAT(num_uploads);
#undef UPDATE_GPU_STAT
#undef UPDATE_COUNTER
ResetStatistics();
}

View file

@ -20,6 +20,8 @@
#include <tuple> #include <tuple>
#include <vector> #include <vector>
class SmallStringBase;
class StateWrapper; class StateWrapper;
class GPUDevice; class GPUDevice;
@ -99,6 +101,10 @@ public:
// Render statistics debug window. // Render statistics debug window.
void DrawDebugStateWindow(); void DrawDebugStateWindow();
void GetStatsString(SmallStringBase& str);
void GetMemoryStatsString(SmallStringBase& str);
void ResetStatistics();
void UpdateStatistics(u32 frame_count);
void CPUClockChanged(); void CPUClockChanged();
@ -308,7 +314,7 @@ protected:
virtual void DispatchRenderCommand(); virtual void DispatchRenderCommand();
virtual void ClearDisplay(); virtual void ClearDisplay();
virtual void UpdateDisplay(); virtual void UpdateDisplay();
virtual void DrawRendererStats(bool is_idle_frame); virtual void DrawRendererStats();
ALWAYS_INLINE void AddDrawTriangleTicks(s32 x1, s32 y1, s32 x2, s32 y2, s32 x3, s32 y3, bool shaded, bool textured, ALWAYS_INLINE void AddDrawTriangleTicks(s32 x1, s32 y1, s32 x2, s32 y2, s32 x3, s32 y3, bool shaded, bool textured,
bool semitransparent) bool semitransparent)
@ -589,17 +595,30 @@ protected:
s32 m_display_texture_view_width = 0; s32 m_display_texture_view_width = 0;
s32 m_display_texture_view_height = 0; s32 m_display_texture_view_height = 0;
struct Stats struct Counters
{ {
u32 num_vram_reads; u32 num_reads;
u32 num_vram_fills; u32 num_writes;
u32 num_vram_writes; u32 num_copies;
u32 num_vram_copies;
u32 num_vertices; u32 num_vertices;
u32 num_polygons; u32 num_primitives;
// u32 num_read_texture_updates;
// u32 num_ubo_updates;
}; };
struct Stats : Counters
{
size_t host_buffer_streamed;
u32 host_num_draws;
u32 host_num_render_passes;
u32 host_num_copies;
u32 host_num_downloads;
u32 host_num_uploads;
};
Counters m_counters = {};
Stats m_stats = {}; Stats m_stats = {};
Stats m_last_stats = {};
private: private:
bool CompileDisplayPipeline(); bool CompileDisplayPipeline();

View file

@ -352,8 +352,8 @@ bool GPU::HandleRenderPolygonCommand()
SetTexturePalette(Truncate16(FifoPeek(2) >> 16)); SetTexturePalette(Truncate16(FifoPeek(2) >> 16));
} }
m_stats.num_vertices += num_vertices; m_counters.num_vertices += num_vertices;
m_stats.num_polygons++; m_counters.num_primitives++;
m_render_command.bits = rc.bits; m_render_command.bits = rc.bits;
m_fifo.RemoveOne(); m_fifo.RemoveOne();
@ -384,8 +384,8 @@ bool GPU::HandleRenderRectangleCommand()
rc.texture_enable ? "textured" : "non-textured", rc.shading_enable ? "shaded" : "monochrome", rc.texture_enable ? "textured" : "non-textured", rc.shading_enable ? "shaded" : "monochrome",
total_words, setup_ticks); total_words, setup_ticks);
m_stats.num_vertices++; m_counters.num_vertices++;
m_stats.num_polygons++; m_counters.num_primitives++;
m_render_command.bits = rc.bits; m_render_command.bits = rc.bits;
m_fifo.RemoveOne(); m_fifo.RemoveOne();
@ -406,8 +406,8 @@ bool GPU::HandleRenderLineCommand()
Log_TracePrintf("Render %s %s line (%u total words)", rc.transparency_enable ? "semi-transparent" : "opaque", Log_TracePrintf("Render %s %s line (%u total words)", rc.transparency_enable ? "semi-transparent" : "opaque",
rc.shading_enable ? "shaded" : "monochrome", total_words); rc.shading_enable ? "shaded" : "monochrome", total_words);
m_stats.num_vertices += 2; m_counters.num_vertices += 2;
m_stats.num_polygons++; m_counters.num_primitives++;
m_render_command.bits = rc.bits; m_render_command.bits = rc.bits;
m_fifo.RemoveOne(); m_fifo.RemoveOne();
@ -468,7 +468,7 @@ bool GPU::HandleFillRectangleCommand()
if (width > 0 && height > 0) if (width > 0 && height > 0)
FillVRAM(dst_x, dst_y, width, height, color); FillVRAM(dst_x, dst_y, width, height, color);
m_stats.num_vram_fills++; m_counters.num_writes++;
AddCommandTicks(46 + ((width / 8) + 9) * height); AddCommandTicks(46 + ((width / 8) + 9) * height);
EndCommand(); EndCommand();
return true; return true;
@ -552,10 +552,10 @@ void GPU::FinishVRAMWrite()
} }
} }
m_counters.num_writes++;
m_blit_buffer.clear(); m_blit_buffer.clear();
m_vram_transfer = {}; m_vram_transfer = {};
m_blitter_state = BlitterState::Idle; m_blitter_state = BlitterState::Idle;
m_stats.num_vram_writes++;
} }
bool GPU::HandleCopyRectangleVRAMToCPUCommand() bool GPU::HandleCopyRectangleVRAMToCPUCommand()
@ -586,7 +586,6 @@ bool GPU::HandleCopyRectangleVRAMToCPUCommand()
} }
// switch to pixel-by-pixel read state // switch to pixel-by-pixel read state
m_stats.num_vram_reads++;
m_blitter_state = BlitterState::ReadingVRAM; m_blitter_state = BlitterState::ReadingVRAM;
m_command_total_words = 0; m_command_total_words = 0;
return true; return true;
@ -612,11 +611,12 @@ bool GPU::HandleCopyRectangleVRAMToVRAMCommand()
width == 0 || height == 0 || (src_x == dst_x && src_y == dst_y && !m_GPUSTAT.set_mask_while_drawing); width == 0 || height == 0 || (src_x == dst_x && src_y == dst_y && !m_GPUSTAT.set_mask_while_drawing);
if (!skip_copy) if (!skip_copy)
{ {
m_counters.num_copies++;
FlushRender(); FlushRender();
CopyVRAM(src_x, src_y, dst_x, dst_y, width, height); CopyVRAM(src_x, src_y, dst_x, dst_y, width, height);
} }
m_stats.num_vram_copies++;
AddCommandTicks(width * height * 2); AddCommandTicks(width * height * 2);
EndCommand(); EndCommand();
return true; return true;

View file

@ -1262,7 +1262,7 @@ void GPU_HW::UpdateVRAMReadTexture(bool drawn, bool written)
scaled_rect.GetWidth(), scaled_rect.GetHeight()); scaled_rect.GetWidth(), scaled_rect.GetHeight());
} }
m_renderer_stats.num_vram_read_texture_updates++; // m_counters.num_read_texture_updates++;
rect.SetInvalid(); rect.SetInvalid();
}; };
@ -2797,7 +2797,7 @@ void GPU_HW::FlushRender()
if (m_batch_ubo_dirty) if (m_batch_ubo_dirty)
{ {
g_gpu_device->UploadUniformBuffer(&m_batch_ubo_data, sizeof(m_batch_ubo_data)); g_gpu_device->UploadUniformBuffer(&m_batch_ubo_data, sizeof(m_batch_ubo_data));
m_renderer_stats.num_uniform_buffer_updates++; // m_counters.num_ubo_updates++;
m_batch_ubo_dirty = false; m_batch_ubo_dirty = false;
} }
@ -2805,20 +2805,17 @@ void GPU_HW::FlushRender()
{ {
if (NeedsTwoPassRendering()) if (NeedsTwoPassRendering())
{ {
m_renderer_stats.num_batches += 2;
DrawBatchVertices(BatchRenderMode::OnlyOpaque, vertex_count, m_batch_base_vertex); DrawBatchVertices(BatchRenderMode::OnlyOpaque, vertex_count, m_batch_base_vertex);
DrawBatchVertices(BatchRenderMode::OnlyTransparent, vertex_count, m_batch_base_vertex); DrawBatchVertices(BatchRenderMode::OnlyTransparent, vertex_count, m_batch_base_vertex);
} }
else else
{ {
m_renderer_stats.num_batches++;
DrawBatchVertices(m_batch.GetRenderMode(), vertex_count, m_batch_base_vertex); DrawBatchVertices(m_batch.GetRenderMode(), vertex_count, m_batch_base_vertex);
} }
} }
if (m_wireframe_mode != GPUWireframeMode::Disabled) if (m_wireframe_mode != GPUWireframeMode::Disabled)
{ {
m_renderer_stats.num_batches++;
g_gpu_device->SetPipeline(m_wireframe_pipeline.get()); g_gpu_device->SetPipeline(m_wireframe_pipeline.get());
g_gpu_device->Draw(vertex_count, m_batch_base_vertex); g_gpu_device->Draw(vertex_count, m_batch_base_vertex);
} }
@ -3094,19 +3091,12 @@ void GPU_HW::DownsampleFramebufferBoxFilter(GPUTexture* source, u32 left, u32 to
SetDisplayTexture(m_downsample_texture.get(), 0, 0, ds_width, ds_height); SetDisplayTexture(m_downsample_texture.get(), 0, 0, ds_width, ds_height);
} }
void GPU_HW::DrawRendererStats(bool is_idle_frame) void GPU_HW::DrawRendererStats()
{ {
if (!is_idle_frame)
{
m_last_renderer_stats = m_renderer_stats;
m_renderer_stats = {};
}
if (ImGui::CollapsingHeader("Renderer Statistics", ImGuiTreeNodeFlags_DefaultOpen)) if (ImGui::CollapsingHeader("Renderer Statistics", ImGuiTreeNodeFlags_DefaultOpen))
{ {
static const ImVec4 active_color{1.0f, 1.0f, 1.0f, 1.0f}; static const ImVec4 active_color{1.0f, 1.0f, 1.0f, 1.0f};
static const ImVec4 inactive_color{0.4f, 0.4f, 0.4f, 1.0f}; static const ImVec4 inactive_color{0.4f, 0.4f, 0.4f, 1.0f};
const auto& stats = m_last_renderer_stats;
ImGui::Columns(2); ImGui::Columns(2);
ImGui::SetColumnWidth(0, 200.0f * Host::GetOSDScale()); ImGui::SetColumnWidth(0, 200.0f * Host::GetOSDScale());
@ -3158,21 +3148,6 @@ void GPU_HW::DrawRendererStats(bool is_idle_frame)
"Cache"); "Cache");
ImGui::NextColumn(); ImGui::NextColumn();
ImGui::TextUnformatted("Batches Drawn:");
ImGui::NextColumn();
ImGui::Text("%u", stats.num_batches);
ImGui::NextColumn();
ImGui::TextUnformatted("VRAM Read Texture Updates:");
ImGui::NextColumn();
ImGui::Text("%u", stats.num_vram_read_texture_updates);
ImGui::NextColumn();
ImGui::TextUnformatted("Uniform Buffer Updates: ");
ImGui::NextColumn();
ImGui::Text("%u", stats.num_uniform_buffer_updates);
ImGui::NextColumn();
ImGui::Columns(1); ImGui::Columns(1);
} }
} }

View file

@ -188,7 +188,7 @@ private:
void CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32 height) override; void CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32 height) override;
void DispatchRenderCommand() override; void DispatchRenderCommand() override;
void FlushRender() override; void FlushRender() override;
void DrawRendererStats(bool is_idle_frame) override; void DrawRendererStats() override;
bool BlitVRAMReplacementTexture(const TextureReplacementTexture* tex, u32 dst_x, u32 dst_y, u32 width, u32 height); bool BlitVRAMReplacementTexture(const TextureReplacementTexture* tex, u32 dst_x, u32 dst_y, u32 width, u32 height);
@ -297,8 +297,4 @@ private:
std::unique_ptr<GPUSampler> m_downsample_lod_sampler; std::unique_ptr<GPUSampler> m_downsample_lod_sampler;
std::unique_ptr<GPUSampler> m_downsample_composite_sampler; std::unique_ptr<GPUSampler> m_downsample_composite_sampler;
u32 m_downsample_scale_or_levels = 0; u32 m_downsample_scale_or_levels = 0;
// Statistics
RendererStats m_renderer_stats = {};
RendererStats m_last_renderer_stats = {};
}; };

View file

@ -253,8 +253,8 @@ void ImGuiManager::FormatProcessorStat(SmallStringBase& text, double usage, doub
void ImGuiManager::DrawPerformanceOverlay() void ImGuiManager::DrawPerformanceOverlay()
{ {
if (!(g_settings.display_show_fps || g_settings.display_show_speed || g_settings.display_show_resolution || if (!(g_settings.display_show_fps || g_settings.display_show_speed || g_settings.display_show_gpu_stats ||
g_settings.display_show_cpu || g_settings.display_show_resolution || g_settings.display_show_cpu_usage ||
(g_settings.display_show_status_indicators && (g_settings.display_show_status_indicators &&
(System::IsPaused() || System::IsFastForwardEnabled() || System::IsTurboEnabled())))) (System::IsPaused() || System::IsFastForwardEnabled() || System::IsTurboEnabled()))))
{ {
@ -322,6 +322,15 @@ void ImGuiManager::DrawPerformanceOverlay()
DRAW_LINE(fixed_font, text, color); DRAW_LINE(fixed_font, text, color);
} }
if (g_settings.display_show_gpu_stats)
{
g_gpu->GetStatsString(text);
DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255));
g_gpu->GetMemoryStatsString(text);
DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255));
}
if (g_settings.display_show_resolution) if (g_settings.display_show_resolution)
{ {
// TODO: this seems wrong? // TODO: this seems wrong?
@ -333,7 +342,7 @@ void ImGuiManager::DrawPerformanceOverlay()
DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255)); DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255));
} }
if (g_settings.display_show_cpu) if (g_settings.display_show_cpu_usage)
{ {
text.format("{:.2f}ms | {:.2f}ms | {:.2f}ms", System::GetMinimumFrameTime(), System::GetAverageFrameTime(), text.format("{:.2f}ms | {:.2f}ms | {:.2f}ms", System::GetMinimumFrameTime(), System::GetAverageFrameTime(),
System::GetMaximumFrameTime()); System::GetMaximumFrameTime());
@ -405,16 +414,10 @@ void ImGuiManager::DrawPerformanceOverlay()
#endif #endif
} }
if (g_settings.display_show_gpu) if (g_settings.display_show_gpu_usage && g_gpu_device->IsGPUTimingEnabled())
{ {
if (g_gpu_device->IsGPUTimingEnabled()) text.assign("GPU: ");
{ FormatProcessorStat(text, System::GetGPUUsage(), System::GetGPUAverageTime());
text.assign("GPU: ");
FormatProcessorStat(text, System::GetGPUUsage(), System::GetGPUAverageTime());
DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255));
}
text.format("VRAM: {} MB", (g_gpu_device->GetVRAMUsage() + (1048576 - 1)) / 1048576);
DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255)); DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255));
} }
@ -525,10 +528,14 @@ void ImGuiManager::DrawEnhancementsOverlay()
{ {
text.append_format(" {}x{}", g_settings.gpu_multisamples, g_settings.gpu_per_sample_shading ? "SSAA" : "MSAA"); text.append_format(" {}x{}", g_settings.gpu_multisamples, g_settings.gpu_per_sample_shading ? "SSAA" : "MSAA");
} }
if (g_settings.gpu_true_color) { if (g_settings.gpu_true_color)
if (g_settings.gpu_debanding) { {
if (g_settings.gpu_debanding)
{
text.append(" TrueColDeband"); text.append(" TrueColDeband");
} else { }
else
{
text.append(" TrueCol"); text.append(" TrueCol");
} }
} }

View file

@ -253,9 +253,10 @@ void Settings::Load(SettingsInterface& si)
display_show_osd_messages = si.GetBoolValue("Display", "ShowOSDMessages", true); display_show_osd_messages = si.GetBoolValue("Display", "ShowOSDMessages", true);
display_show_fps = si.GetBoolValue("Display", "ShowFPS", false); display_show_fps = si.GetBoolValue("Display", "ShowFPS", false);
display_show_speed = si.GetBoolValue("Display", "ShowSpeed", false); display_show_speed = si.GetBoolValue("Display", "ShowSpeed", false);
display_show_gpu_stats = si.GetBoolValue("Display", "ShowGPUStatistics", false);
display_show_resolution = si.GetBoolValue("Display", "ShowResolution", false); display_show_resolution = si.GetBoolValue("Display", "ShowResolution", false);
display_show_cpu = si.GetBoolValue("Display", "ShowCPU", false); display_show_cpu_usage = si.GetBoolValue("Display", "ShowCPU", false);
display_show_gpu = si.GetBoolValue("Display", "ShowGPU", false); display_show_gpu_usage = si.GetBoolValue("Display", "ShowGPU", false);
display_show_frame_times = si.GetBoolValue("Display", "ShowFrameTimes", false); display_show_frame_times = si.GetBoolValue("Display", "ShowFrameTimes", false);
display_show_status_indicators = si.GetBoolValue("Display", "ShowStatusIndicators", true); display_show_status_indicators = si.GetBoolValue("Display", "ShowStatusIndicators", true);
display_show_inputs = si.GetBoolValue("Display", "ShowInputs", false); display_show_inputs = si.GetBoolValue("Display", "ShowInputs", false);
@ -495,8 +496,9 @@ void Settings::Save(SettingsInterface& si) const
si.SetBoolValue("Display", "ShowFPS", display_show_fps); si.SetBoolValue("Display", "ShowFPS", display_show_fps);
si.SetBoolValue("Display", "ShowSpeed", display_show_speed); si.SetBoolValue("Display", "ShowSpeed", display_show_speed);
si.SetBoolValue("Display", "ShowResolution", display_show_resolution); si.SetBoolValue("Display", "ShowResolution", display_show_resolution);
si.SetBoolValue("Display", "ShowCPU", display_show_cpu); si.SetBoolValue("Display", "ShowGPUStatistics", display_show_gpu_stats);
si.SetBoolValue("Display", "ShowGPU", display_show_gpu); si.SetBoolValue("Display", "ShowCPU", display_show_cpu_usage);
si.SetBoolValue("Display", "ShowGPU", display_show_gpu_usage);
si.SetBoolValue("Display", "ShowFrameTimes", display_show_frame_times); si.SetBoolValue("Display", "ShowFrameTimes", display_show_frame_times);
si.SetBoolValue("Display", "ShowStatusIndicators", display_show_status_indicators); si.SetBoolValue("Display", "ShowStatusIndicators", display_show_status_indicators);
si.SetBoolValue("Display", "ShowInputs", display_show_inputs); si.SetBoolValue("Display", "ShowInputs", display_show_inputs);

View file

@ -144,9 +144,10 @@ struct Settings
bool display_show_osd_messages = true; bool display_show_osd_messages = true;
bool display_show_fps = false; bool display_show_fps = false;
bool display_show_speed = false; bool display_show_speed = false;
bool display_show_gpu_stats = false;
bool display_show_resolution = false; bool display_show_resolution = false;
bool display_show_cpu = false; bool display_show_cpu_usage = false;
bool display_show_gpu = false; bool display_show_gpu_usage = false;
bool display_show_frame_times = false; bool display_show_frame_times = false;
bool display_show_status_indicators = true; bool display_show_status_indicators = true;
bool display_show_inputs = false; bool display_show_inputs = false;

View file

@ -952,9 +952,10 @@ void System::SetDefaultSettings(SettingsInterface& si)
temp.display_show_osd_messages = g_settings.display_show_osd_messages; temp.display_show_osd_messages = g_settings.display_show_osd_messages;
temp.display_show_fps = g_settings.display_show_fps; temp.display_show_fps = g_settings.display_show_fps;
temp.display_show_speed = g_settings.display_show_speed; temp.display_show_speed = g_settings.display_show_speed;
temp.display_show_gpu_stats = g_settings.display_show_gpu_stats;
temp.display_show_resolution = g_settings.display_show_resolution; temp.display_show_resolution = g_settings.display_show_resolution;
temp.display_show_cpu = g_settings.display_show_cpu; temp.display_show_cpu_usage = g_settings.display_show_cpu_usage;
temp.display_show_gpu = g_settings.display_show_gpu; temp.display_show_gpu_usage = g_settings.display_show_gpu_usage;
temp.display_show_frame_times = g_settings.display_show_frame_times; temp.display_show_frame_times = g_settings.display_show_frame_times;
// keep controller, we reset it elsewhere // keep controller, we reset it elsewhere
@ -2517,7 +2518,8 @@ void System::UpdatePerformanceCounters()
if (time < PERFORMANCE_COUNTER_UPDATE_INTERVAL) if (time < PERFORMANCE_COUNTER_UPDATE_INTERVAL)
return; return;
const float frames_run = static_cast<float>(s_frame_number - s_last_frame_number); const u32 frames_run = s_frame_number - s_last_frame_number;
const float frames_runf = static_cast<float>(frames_run);
const u32 global_tick_counter = TimingEvents::GetGlobalTickCounter(); const u32 global_tick_counter = TimingEvents::GetGlobalTickCounter();
// TODO: Make the math here less rubbish // TODO: Make the math here less rubbish
@ -2525,13 +2527,13 @@ void System::UpdatePerformanceCounters()
100.0 * (1.0 / ((static_cast<double>(ticks_diff) * static_cast<double>(Threading::GetThreadTicksPerSecond())) / 100.0 * (1.0 / ((static_cast<double>(ticks_diff) * static_cast<double>(Threading::GetThreadTicksPerSecond())) /
Common::Timer::GetFrequency() / 1000000000.0)); Common::Timer::GetFrequency() / 1000000000.0));
const double time_divider = 1000.0 * (1.0 / static_cast<double>(Threading::GetThreadTicksPerSecond())) * const double time_divider = 1000.0 * (1.0 / static_cast<double>(Threading::GetThreadTicksPerSecond())) *
(1.0 / static_cast<double>(frames_run)); (1.0 / static_cast<double>(frames_runf));
s_minimum_frame_time = std::exchange(s_minimum_frame_time_accumulator, 0.0f); s_minimum_frame_time = std::exchange(s_minimum_frame_time_accumulator, 0.0f);
s_average_frame_time = std::exchange(s_average_frame_time_accumulator, 0.0f) / frames_run; s_average_frame_time = std::exchange(s_average_frame_time_accumulator, 0.0f) / frames_runf;
s_maximum_frame_time = std::exchange(s_maximum_frame_time_accumulator, 0.0f); s_maximum_frame_time = std::exchange(s_maximum_frame_time_accumulator, 0.0f);
s_vps = static_cast<float>(frames_run / time); s_vps = static_cast<float>(frames_runf / time);
s_last_frame_number = s_frame_number; s_last_frame_number = s_frame_number;
s_fps = static_cast<float>(s_internal_frame_number - s_last_internal_frame_number) / time; s_fps = static_cast<float>(s_internal_frame_number - s_last_internal_frame_number) / time;
s_last_internal_frame_number = s_internal_frame_number; s_last_internal_frame_number = s_internal_frame_number;
@ -2563,6 +2565,9 @@ void System::UpdatePerformanceCounters()
s_accumulated_gpu_time = 0.0f; s_accumulated_gpu_time = 0.0f;
s_presents_since_last_update = 0; s_presents_since_last_update = 0;
if (g_settings.display_show_gpu_stats)
g_gpu->UpdateStatistics(frames_run);
Log_VerbosePrintf("FPS: %.2f VPS: %.2f CPU: %.2f GPU: %.2f Average: %.2fms Min: %.2fms Max: %.2f ms", s_fps, s_vps, Log_VerbosePrintf("FPS: %.2f VPS: %.2f CPU: %.2f GPU: %.2f Average: %.2fms Min: %.2fms Max: %.2f ms", s_fps, s_vps,
s_cpu_thread_usage, s_gpu_usage, s_average_frame_time, s_minimum_frame_time, s_maximum_frame_time); s_cpu_thread_usage, s_gpu_usage, s_average_frame_time, s_minimum_frame_time, s_maximum_frame_time);
@ -3639,7 +3644,7 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
g_settings.display_aspect_ratio != old_settings.display_aspect_ratio || g_settings.display_aspect_ratio != old_settings.display_aspect_ratio ||
g_settings.display_alignment != old_settings.display_alignment || g_settings.display_alignment != old_settings.display_alignment ||
g_settings.display_scaling != old_settings.display_scaling || g_settings.display_scaling != old_settings.display_scaling ||
g_settings.display_show_gpu != old_settings.display_show_gpu || g_settings.display_show_gpu_usage != old_settings.display_show_gpu_usage ||
g_settings.gpu_pgxp_enable != old_settings.gpu_pgxp_enable || g_settings.gpu_pgxp_enable != old_settings.gpu_pgxp_enable ||
g_settings.gpu_pgxp_texture_correction != old_settings.gpu_pgxp_texture_correction || g_settings.gpu_pgxp_texture_correction != old_settings.gpu_pgxp_texture_correction ||
g_settings.gpu_pgxp_color_correction != old_settings.gpu_pgxp_color_correction || g_settings.gpu_pgxp_color_correction != old_settings.gpu_pgxp_color_correction ||
@ -3678,6 +3683,9 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
CPU::CodeCache::Reset(); CPU::CodeCache::Reset();
} }
if (g_settings.display_show_gpu_stats != old_settings.display_show_gpu_stats)
g_gpu->ResetStatistics();
if (g_settings.cdrom_readahead_sectors != old_settings.cdrom_readahead_sectors) if (g_settings.cdrom_readahead_sectors != old_settings.cdrom_readahead_sectors)
CDROM::SetReadaheadSectors(g_settings.cdrom_readahead_sectors); CDROM::SetReadaheadSectors(g_settings.cdrom_readahead_sectors);

View file

@ -273,6 +273,7 @@ void AdvancedSettingsWidget::addTweakOptions()
addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Show Status Indicators"), "Display", addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Show Status Indicators"), "Display",
"ShowStatusIndicators", true); "ShowStatusIndicators", true);
addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Show Frame Times"), "Display", "ShowFrameTimes", false); addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Show Frame Times"), "Display", "ShowFrameTimes", false);
addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Show Settings Overlay"), "Display", "ShowEnhancements", false);
addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Apply Compatibility Settings"), "Main", addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Apply Compatibility Settings"), "Main",
"ApplyCompatibilitySettings", true); "ApplyCompatibilitySettings", true);
addIntRangeTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Display FPS Limit"), "Display", "MaxFPS", 0, 1000, 0); addIntRangeTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Display FPS Limit"), "Display", "MaxFPS", 0, 1000, 0);
@ -384,6 +385,7 @@ void AdvancedSettingsWidget::onResetToDefaultClicked()
setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Disable all enhancements setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Disable all enhancements
setBooleanTweakOption(m_ui.tweakOptionTable, i++, true); // Show status indicators setBooleanTweakOption(m_ui.tweakOptionTable, i++, true); // Show status indicators
setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Show frame times setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Show frame times
setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Show settings overlay
setBooleanTweakOption(m_ui.tweakOptionTable, i++, true); // Apply compatibility settings setBooleanTweakOption(m_ui.tweakOptionTable, i++, true); // Apply compatibility settings
setIntRangeTweakOption(m_ui.tweakOptionTable, i++, 0); // Display FPS limit setIntRangeTweakOption(m_ui.tweakOptionTable, i++, 0); // Display FPS limit
setChoiceTweakOption(m_ui.tweakOptionTable, i++, Settings::DEFAULT_DISPLAY_EXCLUSIVE_FULLSCREEN_CONTROL); setChoiceTweakOption(m_ui.tweakOptionTable, i++, Settings::DEFAULT_DISPLAY_EXCLUSIVE_FULLSCREEN_CONTROL);
@ -438,6 +440,7 @@ void AdvancedSettingsWidget::onResetToDefaultClicked()
sif->DeleteValue("Display", "ShowEnhancements"); sif->DeleteValue("Display", "ShowEnhancements");
sif->DeleteValue("Display", "ShowStatusIndicators"); sif->DeleteValue("Display", "ShowStatusIndicators");
sif->DeleteValue("Display", "ShowFrameTimes"); sif->DeleteValue("Display", "ShowFrameTimes");
sif->DeleteValue("Display", "ShowEnhancements");
sif->DeleteValue("Main", "ApplyCompatibilitySettings"); sif->DeleteValue("Main", "ApplyCompatibilitySettings");
sif->DeleteValue("Display", "MaxFPS"); sif->DeleteValue("Display", "MaxFPS");
sif->DeleteValue("Display", "ActiveStartOffset"); sif->DeleteValue("Display", "ActiveStartOffset");

View file

@ -56,8 +56,8 @@ DisplaySettingsWidget::DisplaySettingsWidget(SettingsWindow* dialog, QWidget* pa
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showCPU, "Display", "ShowCPU", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showCPU, "Display", "ShowCPU", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showGPU, "Display", "ShowGPU", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showGPU, "Display", "ShowGPU", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showInput, "Display", "ShowInputs", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showInput, "Display", "ShowInputs", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showSettings, "Display", "ShowEnhancements", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showGPUStatistics, "Display", "ShowGPUStatistics", false);
connect(m_ui.renderer, QOverload<int>::of(&QComboBox::currentIndexChanged), this, connect(m_ui.renderer, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&DisplaySettingsWidget::populateGPUAdaptersAndResolutions); &DisplaySettingsWidget::populateGPUAdaptersAndResolutions);
connect(m_ui.adapter, QOverload<int>::of(&QComboBox::currentIndexChanged), this, connect(m_ui.adapter, QOverload<int>::of(&QComboBox::currentIndexChanged), this,

View file

@ -241,9 +241,9 @@
</widget> </widget>
</item> </item>
<item row="5" column="1"> <item row="5" column="1">
<widget class="QCheckBox" name="showSettings"> <widget class="QCheckBox" name="showGPUStatistics">
<property name="text"> <property name="text">
<string>Show Settings Overlay</string> <string>Show GPU Statistics</string>
</property> </property>
</widget> </widget>
</item> </item>

View file

@ -513,6 +513,8 @@ void D3D11Device::CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 d
src11->CommitClear(m_context.Get()); src11->CommitClear(m_context.Get());
s_stats.num_copies++;
const CD3D11_BOX src_box(static_cast<LONG>(src_x), static_cast<LONG>(src_y), 0, static_cast<LONG>(src_x + width), const CD3D11_BOX src_box(static_cast<LONG>(src_x), static_cast<LONG>(src_y), 0, static_cast<LONG>(src_x + width),
static_cast<LONG>(src_y + height), 1); static_cast<LONG>(src_y + height), 1);
m_context->CopySubresourceRegion(dst11->GetD3DTexture(), D3D11CalcSubresource(dst_level, dst_layer, dst->GetLevels()), m_context->CopySubresourceRegion(dst11->GetD3DTexture(), D3D11CalcSubresource(dst_level, dst_layer, dst->GetLevels()),
@ -531,6 +533,8 @@ void D3D11Device::ResolveTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u3
DebugAssert((dst_y + height) <= dst->GetMipHeight(dst_level)); DebugAssert((dst_y + height) <= dst->GetMipHeight(dst_level));
DebugAssert(!dst->IsMultisampled() && src->IsMultisampled()); DebugAssert(!dst->IsMultisampled() && src->IsMultisampled());
s_stats.num_copies++;
// DX11 can't resolve partial rects. // DX11 can't resolve partial rects.
Assert(src_x == 0 && src_y == 0 && width == src->GetWidth() && height == src->GetHeight() && dst_x == 0 && Assert(src_x == 0 && src_y == 0 && width == src->GetWidth() && height == src->GetHeight() && dst_x == 0 &&
dst_y == 0 && width == dst->GetMipWidth(dst_level) && height == dst->GetMipHeight(dst_level)); dst_y == 0 && width == dst->GetMipWidth(dst_level) && height == dst->GetMipHeight(dst_level));
@ -635,6 +639,7 @@ bool D3D11Device::BeginPresent(bool skip_present)
static constexpr float clear_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; static constexpr float clear_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
m_context->ClearRenderTargetView(m_swap_chain_rtv.Get(), clear_color); m_context->ClearRenderTargetView(m_swap_chain_rtv.Get(), clear_color);
m_context->OMSetRenderTargets(1, m_swap_chain_rtv.GetAddressOf(), nullptr); m_context->OMSetRenderTargets(1, m_swap_chain_rtv.GetAddressOf(), nullptr);
s_stats.num_render_passes++;
m_num_current_render_targets = 0; m_num_current_render_targets = 0;
std::memset(m_current_render_targets.data(), 0, sizeof(m_current_render_targets)); std::memset(m_current_render_targets.data(), 0, sizeof(m_current_render_targets));
m_current_depth_target = nullptr; m_current_depth_target = nullptr;
@ -852,7 +857,9 @@ void D3D11Device::MapVertexBuffer(u32 vertex_size, u32 vertex_count, void** map_
void D3D11Device::UnmapVertexBuffer(u32 vertex_size, u32 vertex_count) void D3D11Device::UnmapVertexBuffer(u32 vertex_size, u32 vertex_count)
{ {
m_vertex_buffer.Unmap(m_context.Get(), vertex_size * vertex_count); const u32 upload_size = vertex_size * vertex_count;
s_stats.buffer_streamed += upload_size;
m_vertex_buffer.Unmap(m_context.Get(), upload_size);
} }
void D3D11Device::MapIndexBuffer(u32 index_count, DrawIndex** map_ptr, u32* map_space, u32* map_base_index) void D3D11Device::MapIndexBuffer(u32 index_count, DrawIndex** map_ptr, u32* map_space, u32* map_base_index)
@ -865,6 +872,7 @@ void D3D11Device::MapIndexBuffer(u32 index_count, DrawIndex** map_ptr, u32* map_
void D3D11Device::UnmapIndexBuffer(u32 used_index_count) void D3D11Device::UnmapIndexBuffer(u32 used_index_count)
{ {
s_stats.buffer_streamed += sizeof(DrawIndex) * used_index_count;
m_index_buffer.Unmap(m_context.Get(), sizeof(DrawIndex) * used_index_count); m_index_buffer.Unmap(m_context.Get(), sizeof(DrawIndex) * used_index_count);
} }
@ -874,6 +882,7 @@ void D3D11Device::PushUniformBuffer(const void* data, u32 data_size)
const auto res = m_uniform_buffer.Map(m_context.Get(), UNIFORM_BUFFER_ALIGNMENT, used_space); const auto res = m_uniform_buffer.Map(m_context.Get(), UNIFORM_BUFFER_ALIGNMENT, used_space);
std::memcpy(res.pointer, data, data_size); std::memcpy(res.pointer, data, data_size);
m_uniform_buffer.Unmap(m_context.Get(), data_size); m_uniform_buffer.Unmap(m_context.Get(), data_size);
s_stats.buffer_streamed += data_size;
const UINT first_constant = (res.index_aligned * UNIFORM_BUFFER_ALIGNMENT) / 16u; const UINT first_constant = (res.index_aligned * UNIFORM_BUFFER_ALIGNMENT) / 16u;
const UINT num_constants = (used_space * UNIFORM_BUFFER_ALIGNMENT) / 16u; const UINT num_constants = (used_space * UNIFORM_BUFFER_ALIGNMENT) / 16u;
@ -895,6 +904,8 @@ void D3D11Device::UnmapUniformBuffer(u32 size)
const UINT num_constants = used_space / 16u; const UINT num_constants = used_space / 16u;
m_uniform_buffer.Unmap(m_context.Get(), used_space); m_uniform_buffer.Unmap(m_context.Get(), used_space);
s_stats.buffer_streamed += size;
m_context->VSSetConstantBuffers1(0, 1, m_uniform_buffer.GetD3DBufferArray(), &first_constant, &num_constants); m_context->VSSetConstantBuffers1(0, 1, m_uniform_buffer.GetD3DBufferArray(), &first_constant, &num_constants);
m_context->PSSetConstantBuffers1(0, 1, m_uniform_buffer.GetD3DBufferArray(), &first_constant, &num_constants); m_context->PSSetConstantBuffers1(0, 1, m_uniform_buffer.GetD3DBufferArray(), &first_constant, &num_constants);
} }
@ -944,6 +955,7 @@ void D3D11Device::SetRenderTargets(GPUTexture* const* rts, u32 num_rts, GPUTextu
if (!changed) if (!changed)
return; return;
s_stats.num_render_passes++;
m_context->OMSetRenderTargets(num_rts, rtvs, ds ? static_cast<D3D11Texture*>(ds)->GetD3DDSV() : nullptr); m_context->OMSetRenderTargets(num_rts, rtvs, ds ? static_cast<D3D11Texture*>(ds)->GetD3DDSV() : nullptr);
} }
@ -1035,10 +1047,12 @@ void D3D11Device::SetScissor(s32 x, s32 y, s32 width, s32 height)
void D3D11Device::Draw(u32 vertex_count, u32 base_vertex) void D3D11Device::Draw(u32 vertex_count, u32 base_vertex)
{ {
s_stats.num_draws++;
m_context->Draw(vertex_count, base_vertex); m_context->Draw(vertex_count, base_vertex);
} }
void D3D11Device::DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) void D3D11Device::DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex)
{ {
s_stats.num_draws++;
m_context->DrawIndexed(index_count, base_index, base_vertex); m_context->DrawIndexed(index_count, base_index, base_vertex);
} }

View file

@ -45,6 +45,8 @@ bool D3D11Device::DownloadTexture(GPUTexture* texture, u32 x, u32 y, u32 width,
return false; return false;
} }
s_stats.num_downloads++;
const u32 copy_size = tex->GetPixelSize() * width; const u32 copy_size = tex->GetPixelSize() * width;
StringUtil::StrideMemCpy(out_data, out_data_stride, sr.pData, sr.RowPitch, copy_size, height); StringUtil::StrideMemCpy(out_data, out_data_stride, sr.pData, sr.RowPitch, copy_size, height);
m_context->Unmap(m_readback_staging_texture.Get(), 0); m_context->Unmap(m_readback_staging_texture.Get(), 0);
@ -218,6 +220,10 @@ bool D3D11Texture::Update(u32 x, u32 y, u32 width, u32 height, const void* data,
ID3D11DeviceContext1* context = D3D11Device::GetD3DContext(); ID3D11DeviceContext1* context = D3D11Device::GetD3DContext();
CommitClear(context); CommitClear(context);
GPUDevice::GetStatistics().buffer_streamed += height * pitch;
GPUDevice::GetStatistics().num_uploads++;
context->UpdateSubresource(m_texture.Get(), srnum, &box, data, pitch, 0); context->UpdateSubresource(m_texture.Get(), srnum, &box, data, pitch, 0);
m_state = GPUTexture::State::Dirty; m_state = GPUTexture::State::Dirty;
return true; return true;
@ -246,6 +252,9 @@ bool D3D11Texture::Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32
return false; return false;
} }
GPUDevice::GetStatistics().buffer_streamed += height * sr.RowPitch;
GPUDevice::GetStatistics().num_uploads++;
*map = static_cast<u8*>(sr.pData) + (y * sr.RowPitch) + (x * GetPixelSize()); *map = static_cast<u8*>(sr.pData) + (y * sr.RowPitch) + (x * GetPixelSize());
*map_stride = sr.RowPitch; *map_stride = sr.RowPitch;
m_mapped_subresource = srnum; m_mapped_subresource = srnum;
@ -323,6 +332,12 @@ std::unique_ptr<D3D11Texture> D3D11Texture::Create(ID3D11Device* device, u32 wid
return nullptr; return nullptr;
} }
if (initial_data)
{
GPUDevice::GetStatistics().buffer_streamed += height * initial_data_stride;
GPUDevice::GetStatistics().num_uploads++;
}
ComPtr<ID3D11ShaderResourceView> srv; ComPtr<ID3D11ShaderResourceView> srv;
if (bind_flags & D3D11_BIND_SHADER_RESOURCE) if (bind_flags & D3D11_BIND_SHADER_RESOURCE)
{ {
@ -412,7 +427,10 @@ void* D3D11TextureBuffer::Map(u32 required_elements)
void D3D11TextureBuffer::Unmap(u32 used_elements) void D3D11TextureBuffer::Unmap(u32 used_elements)
{ {
m_buffer.Unmap(D3D11Device::GetD3DContext(), used_elements * GetElementSize(m_format)); const u32 size = used_elements * GetElementSize(m_format);
GPUDevice::GetStatistics().buffer_streamed += size;
GPUDevice::GetStatistics().num_uploads++;
m_buffer.Unmap(D3D11Device::GetD3DContext(), size);
} }
void D3D11TextureBuffer::SetDebugName(const std::string_view& name) void D3D11TextureBuffer::SetDebugName(const std::string_view& name)

View file

@ -118,7 +118,8 @@ D3D12Device::ComPtr<ID3D12RootSignature> D3D12Device::CreateRootSignature(const
} }
bool D3D12Device::CreateDevice(const std::string_view& adapter, bool threaded_presentation, bool D3D12Device::CreateDevice(const std::string_view& adapter, bool threaded_presentation,
std::optional<bool> exclusive_fullscreen_control, FeatureMask disabled_features, Error* error) std::optional<bool> exclusive_fullscreen_control, FeatureMask disabled_features,
Error* error)
{ {
std::unique_lock lock(s_instance_mutex); std::unique_lock lock(s_instance_mutex);
@ -1256,6 +1257,8 @@ void D3D12Device::CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 d
D->CommitClear(); D->CommitClear();
} }
s_stats.num_copies++;
// *now* we can do a normal image copy. // *now* we can do a normal image copy.
if (InRenderPass()) if (InRenderPass())
EndRenderPass(); EndRenderPass();
@ -1297,6 +1300,8 @@ void D3D12Device::ResolveTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u3
if (InRenderPass()) if (InRenderPass())
EndRenderPass(); EndRenderPass();
s_stats.num_copies++;
D3D12Texture* D = static_cast<D3D12Texture*>(dst); D3D12Texture* D = static_cast<D3D12Texture*>(dst);
D3D12Texture* S = static_cast<D3D12Texture*>(src); D3D12Texture* S = static_cast<D3D12Texture*>(src);
ID3D12GraphicsCommandList4* cmdlist = GetCommandList(); ID3D12GraphicsCommandList4* cmdlist = GetCommandList();
@ -1401,7 +1406,9 @@ void D3D12Device::MapVertexBuffer(u32 vertex_size, u32 vertex_count, void** map_
void D3D12Device::UnmapVertexBuffer(u32 vertex_size, u32 vertex_count) void D3D12Device::UnmapVertexBuffer(u32 vertex_size, u32 vertex_count)
{ {
m_vertex_buffer.CommitMemory(vertex_size * vertex_count); const u32 upload_size = vertex_size * vertex_count;
s_stats.buffer_streamed += upload_size;
m_vertex_buffer.CommitMemory(upload_size);
} }
void D3D12Device::MapIndexBuffer(u32 index_count, DrawIndex** map_ptr, u32* map_space, u32* map_base_index) void D3D12Device::MapIndexBuffer(u32 index_count, DrawIndex** map_ptr, u32* map_space, u32* map_base_index)
@ -1421,7 +1428,9 @@ void D3D12Device::MapIndexBuffer(u32 index_count, DrawIndex** map_ptr, u32* map_
void D3D12Device::UnmapIndexBuffer(u32 used_index_count) void D3D12Device::UnmapIndexBuffer(u32 used_index_count)
{ {
m_index_buffer.CommitMemory(sizeof(DrawIndex) * used_index_count); const u32 upload_size = sizeof(DrawIndex) * used_index_count;
s_stats.buffer_streamed += upload_size;
m_index_buffer.CommitMemory(upload_size);
} }
void D3D12Device::PushUniformBuffer(const void* data, u32 data_size) void D3D12Device::PushUniformBuffer(const void* data, u32 data_size)
@ -1441,6 +1450,7 @@ void D3D12Device::PushUniformBuffer(const void* data, u32 data_size)
UpdateRootSignature(); UpdateRootSignature();
} }
s_stats.buffer_streamed += data_size;
GetCommandList()->SetGraphicsRoot32BitConstants(push_parameter[static_cast<u8>(m_current_pipeline_layout)], GetCommandList()->SetGraphicsRoot32BitConstants(push_parameter[static_cast<u8>(m_current_pipeline_layout)],
data_size / 4u, data, 0); data_size / 4u, data, 0);
} }
@ -1462,6 +1472,7 @@ void* D3D12Device::MapUniformBuffer(u32 size)
void D3D12Device::UnmapUniformBuffer(u32 size) void D3D12Device::UnmapUniformBuffer(u32 size)
{ {
s_stats.buffer_streamed += size;
m_uniform_buffer_position = m_uniform_buffer.GetCurrentOffset(); m_uniform_buffer_position = m_uniform_buffer.GetCurrentOffset();
m_uniform_buffer.CommitMemory(size); m_uniform_buffer.CommitMemory(size);
m_dirty_flags |= DIRTY_FLAG_CONSTANT_BUFFER; m_dirty_flags |= DIRTY_FLAG_CONSTANT_BUFFER;
@ -1681,6 +1692,7 @@ void D3D12Device::BeginRenderPass()
// TODO: Stats // TODO: Stats
m_in_render_pass = true; m_in_render_pass = true;
s_stats.num_render_passes++;
// If this is a new command buffer, bind the pipeline and such. // If this is a new command buffer, bind the pipeline and such.
if (m_dirty_flags & DIRTY_FLAG_INITIAL) if (m_dirty_flags & DIRTY_FLAG_INITIAL)
@ -1715,6 +1727,7 @@ void D3D12Device::BeginSwapChainRenderPass()
m_num_current_render_targets = 0; m_num_current_render_targets = 0;
m_current_depth_target = nullptr; m_current_depth_target = nullptr;
m_in_render_pass = true; m_in_render_pass = true;
s_stats.num_render_passes++;
// Clear pipeline, it's likely incompatible. // Clear pipeline, it's likely incompatible.
m_current_pipeline = nullptr; m_current_pipeline = nullptr;
@ -2119,11 +2132,13 @@ bool D3D12Device::UpdateRootParameters(u32 dirty)
void D3D12Device::Draw(u32 vertex_count, u32 base_vertex) void D3D12Device::Draw(u32 vertex_count, u32 base_vertex)
{ {
PreDrawCheck(); PreDrawCheck();
s_stats.num_draws++;
GetCommandList()->DrawInstanced(vertex_count, 1, base_vertex, 0); GetCommandList()->DrawInstanced(vertex_count, 1, base_vertex, 0);
} }
void D3D12Device::DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) void D3D12Device::DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex)
{ {
PreDrawCheck(); PreDrawCheck();
s_stats.num_draws++;
GetCommandList()->DrawIndexedInstanced(index_count, 1, base_index, base_vertex, 0); GetCommandList()->DrawIndexedInstanced(index_count, 1, base_index, base_vertex, 0);
} }

View file

@ -443,6 +443,9 @@ bool D3D12Texture::Update(u32 x, u32 y, u32 width, u32 height, const void* data,
m_state = State::Dirty; m_state = State::Dirty;
} }
GPUDevice::GetStatistics().buffer_streamed += required_size;
GPUDevice::GetStatistics().num_uploads++;
// first time the texture is used? don't leave it undefined // first time the texture is used? don't leave it undefined
if (m_resource_state == D3D12_RESOURCE_STATE_COMMON) if (m_resource_state == D3D12_RESOURCE_STATE_COMMON)
TransitionToState(cmdlist, D3D12_RESOURCE_STATE_COPY_DEST); TransitionToState(cmdlist, D3D12_RESOURCE_STATE_COPY_DEST);
@ -511,6 +514,9 @@ void D3D12Texture::Unmap()
const u32 offset = sb.GetCurrentOffset(); const u32 offset = sb.GetCurrentOffset();
sb.CommitMemory(req_size); sb.CommitMemory(req_size);
GPUDevice::GetStatistics().buffer_streamed += req_size;
GPUDevice::GetStatistics().num_uploads++;
ID3D12GraphicsCommandList4* cmdlist = GetCommandBufferForUpdate(); ID3D12GraphicsCommandList4* cmdlist = GetCommandBufferForUpdate();
// first time the texture is used? don't leave it undefined // first time the texture is used? don't leave it undefined
@ -907,7 +913,10 @@ void* D3D12TextureBuffer::Map(u32 required_elements)
void D3D12TextureBuffer::Unmap(u32 used_elements) void D3D12TextureBuffer::Unmap(u32 used_elements)
{ {
m_buffer.CommitMemory(GetElementSize(m_format) * used_elements); const u32 size = GetElementSize(m_format) * used_elements;
GPUDevice::GetStatistics().buffer_streamed += size;
GPUDevice::GetStatistics().num_uploads++;
m_buffer.CommitMemory(size);
} }
void D3D12TextureBuffer::SetDebugName(const std::string_view& name) void D3D12TextureBuffer::SetDebugName(const std::string_view& name)

View file

@ -40,6 +40,7 @@ std::unique_ptr<GPUDevice> g_gpu_device;
static std::string s_pipeline_cache_path; static std::string s_pipeline_cache_path;
size_t GPUDevice::s_total_vram_usage = 0; size_t GPUDevice::s_total_vram_usage = 0;
GPUDevice::Statistics GPUDevice::s_stats = {};
GPUSampler::GPUSampler() = default; GPUSampler::GPUSampler() = default;
@ -206,6 +207,11 @@ size_t GPUFramebufferManagerBase::KeyHash::operator()(const Key& key) const
return XXH32(&key, sizeof(key), 0x1337); return XXH32(&key, sizeof(key), 0x1337);
} }
GPUDevice::GPUDevice()
{
ResetStatistics();
}
GPUDevice::~GPUDevice() = default; GPUDevice::~GPUDevice() = default;
RenderAPI GPUDevice::GetPreferredAPI() RenderAPI GPUDevice::GetPreferredAPI()
@ -993,6 +999,11 @@ float GPUDevice::GetAndResetAccumulatedGPUTime()
return 0.0f; return 0.0f;
} }
void GPUDevice::ResetStatistics()
{
s_stats = {};
}
std::unique_ptr<GPUDevice> GPUDevice::CreateDeviceForAPI(RenderAPI api) std::unique_ptr<GPUDevice> GPUDevice::CreateDeviceForAPI(RenderAPI api)
{ {
switch (api) switch (api)

View file

@ -458,6 +458,16 @@ public:
bool prefer_unused_textures : 1; bool prefer_unused_textures : 1;
}; };
struct Statistics
{
size_t buffer_streamed;
u32 num_draws;
u32 num_render_passes;
u32 num_copies;
u32 num_downloads;
u32 num_uploads;
};
struct AdapterAndModeList struct AdapterAndModeList
{ {
std::vector<std::string> adapter_names; std::vector<std::string> adapter_names;
@ -474,6 +484,7 @@ public:
static constexpr u32 MAX_RENDER_TARGETS = 4; static constexpr u32 MAX_RENDER_TARGETS = 4;
static_assert(sizeof(GPUPipeline::GraphicsConfig::color_formats) == sizeof(GPUTexture::Format) * MAX_RENDER_TARGETS); static_assert(sizeof(GPUPipeline::GraphicsConfig::color_formats) == sizeof(GPUTexture::Format) * MAX_RENDER_TARGETS);
GPUDevice();
virtual ~GPUDevice(); virtual ~GPUDevice();
/// Returns the default/preferred API for the system. /// Returns the default/preferred API for the system.
@ -652,6 +663,9 @@ public:
/// Returns the amount of GPU time utilized since the last time this method was called. /// Returns the amount of GPU time utilized since the last time this method was called.
virtual float GetAndResetAccumulatedGPUTime(); virtual float GetAndResetAccumulatedGPUTime();
ALWAYS_INLINE static Statistics& GetStatistics() { return s_stats; }
static void ResetStatistics();
protected: protected:
virtual bool CreateDevice(const std::string_view& adapter, bool threaded_presentation, virtual bool CreateDevice(const std::string_view& adapter, bool threaded_presentation,
std::optional<bool> exclusive_fullscreen_control, FeatureMask disabled_features, std::optional<bool> exclusive_fullscreen_control, FeatureMask disabled_features,
@ -738,6 +752,8 @@ private:
float m_display_frame_interval = 0.0f; float m_display_frame_interval = 0.0f;
protected: protected:
static Statistics s_stats;
bool m_gpu_timing_enabled = false; bool m_gpu_timing_enabled = false;
bool m_vsync_enabled = false; bool m_vsync_enabled = false;
bool m_debug_device = false; bool m_debug_device = false;

View file

@ -890,6 +890,9 @@ bool MetalTexture::Update(u32 x, u32 y, u32 width, u32 height, const void* data,
const u32 aligned_pitch = Common::AlignUpPow2(width * GetPixelSize(), TEXTURE_UPLOAD_PITCH_ALIGNMENT); const u32 aligned_pitch = Common::AlignUpPow2(width * GetPixelSize(), TEXTURE_UPLOAD_PITCH_ALIGNMENT);
const u32 req_size = height * aligned_pitch; const u32 req_size = height * aligned_pitch;
GPUDevice::GetStatistics().buffer_streamed += req_size;
GPUDevice::GetStatistics().num_uploads++;
MetalDevice& dev = MetalDevice::GetInstance(); MetalDevice& dev = MetalDevice::GetInstance();
MetalStreamBuffer& sb = dev.GetTextureStreamBuffer(); MetalStreamBuffer& sb = dev.GetTextureStreamBuffer();
id<MTLBuffer> actual_buffer; id<MTLBuffer> actual_buffer;
@ -989,6 +992,9 @@ void MetalTexture::Unmap()
const u32 aligned_pitch = Common::AlignUpPow2(m_map_width * GetPixelSize(), TEXTURE_UPLOAD_PITCH_ALIGNMENT); const u32 aligned_pitch = Common::AlignUpPow2(m_map_width * GetPixelSize(), TEXTURE_UPLOAD_PITCH_ALIGNMENT);
const u32 req_size = m_map_height * aligned_pitch; const u32 req_size = m_map_height * aligned_pitch;
GPUDevice::GetStatistics().buffer_streamed += req_size;
GPUDevice::GetStatistics().num_uploads++;
MetalDevice& dev = MetalDevice::GetInstance(); MetalDevice& dev = MetalDevice::GetInstance();
MetalStreamBuffer& sb = dev.GetTextureStreamBuffer(); MetalStreamBuffer& sb = dev.GetTextureStreamBuffer();
const u32 offset = sb.GetCurrentOffset(); const u32 offset = sb.GetCurrentOffset();
@ -1206,6 +1212,8 @@ bool MetalDevice::DownloadTexture(GPUTexture* texture, u32 x, u32 y, u32 width,
MetalTexture* T = static_cast<MetalTexture*>(texture); MetalTexture* T = static_cast<MetalTexture*>(texture);
CommitClear(T); CommitClear(T);
s_stats.num_downloads++;
@autoreleasepool @autoreleasepool
{ {
id<MTLBlitCommandEncoder> encoder = GetBlitEncoder(true); id<MTLBlitCommandEncoder> encoder = GetBlitEncoder(true);
@ -1303,6 +1311,8 @@ void MetalDevice::CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 d
S->SetUseFenceCounter(m_current_fence_counter); S->SetUseFenceCounter(m_current_fence_counter);
D->SetUseFenceCounter(m_current_fence_counter); D->SetUseFenceCounter(m_current_fence_counter);
s_stats.num_copies++;
@autoreleasepool @autoreleasepool
{ {
id<MTLBlitCommandEncoder> encoder = GetBlitEncoder(true); id<MTLBlitCommandEncoder> encoder = GetBlitEncoder(true);
@ -1365,6 +1375,8 @@ void MetalDevice::ResolveTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u3
if (InRenderPass()) if (InRenderPass())
EndRenderPass(); EndRenderPass();
s_stats.num_copies++;
const u32 threadgroupHeight = resolve_pipeline.maxTotalThreadsPerThreadgroup / resolve_pipeline.threadExecutionWidth; const u32 threadgroupHeight = resolve_pipeline.maxTotalThreadsPerThreadgroup / resolve_pipeline.threadExecutionWidth;
const MTLSize intrinsicThreadgroupSize = MTLSizeMake(resolve_pipeline.threadExecutionWidth, threadgroupHeight, 1); const MTLSize intrinsicThreadgroupSize = MTLSizeMake(resolve_pipeline.threadExecutionWidth, threadgroupHeight, 1);
const MTLSize threadgroupsInGrid = const MTLSize threadgroupsInGrid =
@ -1472,7 +1484,10 @@ void* MetalTextureBuffer::Map(u32 required_elements)
void MetalTextureBuffer::Unmap(u32 used_elements) void MetalTextureBuffer::Unmap(u32 used_elements)
{ {
m_buffer.CommitMemory(GetElementSize(m_format) * used_elements); const u32 size = GetElementSize(m_format) * used_elements;
GPUDevice::GetStatistics().buffer_streamed += size;
GPUDevice::GetStatistics().num_uploads++;
m_buffer.CommitMemory(size);
} }
void MetalTextureBuffer::SetDebugName(const std::string_view& name) void MetalTextureBuffer::SetDebugName(const std::string_view& name)
@ -1523,7 +1538,9 @@ void MetalDevice::MapVertexBuffer(u32 vertex_size, u32 vertex_count, void** map_
void MetalDevice::UnmapVertexBuffer(u32 vertex_size, u32 vertex_count) void MetalDevice::UnmapVertexBuffer(u32 vertex_size, u32 vertex_count)
{ {
m_vertex_buffer.CommitMemory(vertex_size * vertex_count); const u32 size = vertex_size * vertex_count;
s_stats.buffer_streamed += size;
m_vertex_buffer.CommitMemory(size);
} }
void MetalDevice::MapIndexBuffer(u32 index_count, DrawIndex** map_ptr, u32* map_space, u32* map_base_index) void MetalDevice::MapIndexBuffer(u32 index_count, DrawIndex** map_ptr, u32* map_space, u32* map_base_index)
@ -1543,11 +1560,14 @@ void MetalDevice::MapIndexBuffer(u32 index_count, DrawIndex** map_ptr, u32* map_
void MetalDevice::UnmapIndexBuffer(u32 used_index_count) void MetalDevice::UnmapIndexBuffer(u32 used_index_count)
{ {
m_index_buffer.CommitMemory(sizeof(DrawIndex) * used_index_count); const u32 size = sizeof(DrawIndex) * used_index_count;
s_stats.buffer_streamed += size;
m_index_buffer.CommitMemory(size);
} }
void MetalDevice::PushUniformBuffer(const void* data, u32 data_size) void MetalDevice::PushUniformBuffer(const void* data, u32 data_size)
{ {
s_stats.buffer_streamed += data_size;
void* map = MapUniformBuffer(data_size); void* map = MapUniformBuffer(data_size);
std::memcpy(map, data, data_size); std::memcpy(map, data, data_size);
UnmapUniformBuffer(data_size); UnmapUniformBuffer(data_size);
@ -1568,6 +1588,7 @@ void* MetalDevice::MapUniformBuffer(u32 size)
void MetalDevice::UnmapUniformBuffer(u32 size) void MetalDevice::UnmapUniformBuffer(u32 size)
{ {
s_stats.buffer_streamed += size;
m_current_uniform_buffer_position = m_uniform_buffer.GetCurrentOffset(); m_current_uniform_buffer_position = m_uniform_buffer.GetCurrentOffset();
m_uniform_buffer.CommitMemory(size); m_uniform_buffer.CommitMemory(size);
if (InRenderPass()) if (InRenderPass())
@ -1758,6 +1779,8 @@ void MetalDevice::BeginRenderPass()
m_inline_upload_encoder = nil; m_inline_upload_encoder = nil;
} }
s_stats.num_render_passes++;
@autoreleasepool @autoreleasepool
{ {
MTLRenderPassDescriptor* desc = [MTLRenderPassDescriptor renderPassDescriptor]; MTLRenderPassDescriptor* desc = [MTLRenderPassDescriptor renderPassDescriptor];
@ -1927,6 +1950,7 @@ void MetalDevice::PreDrawCheck()
void MetalDevice::Draw(u32 vertex_count, u32 base_vertex) void MetalDevice::Draw(u32 vertex_count, u32 base_vertex)
{ {
PreDrawCheck(); PreDrawCheck();
s_stats.num_draws++;
[m_render_encoder drawPrimitives:m_current_pipeline->GetPrimitive() vertexStart:base_vertex vertexCount:vertex_count]; [m_render_encoder drawPrimitives:m_current_pipeline->GetPrimitive() vertexStart:base_vertex vertexCount:vertex_count];
} }
@ -1934,6 +1958,8 @@ void MetalDevice::DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex)
{ {
PreDrawCheck(); PreDrawCheck();
s_stats.num_draws++;
const u32 index_offset = base_index * sizeof(u16); const u32 index_offset = base_index * sizeof(u16);
[m_render_encoder drawIndexedPrimitives:m_current_pipeline->GetPrimitive() [m_render_encoder drawIndexedPrimitives:m_current_pipeline->GetPrimitive()
indexCount:index_count indexCount:index_count
@ -2000,6 +2026,7 @@ bool MetalDevice::BeginPresent(bool skip_present)
m_layer_pass_desc.colorAttachments[0].texture = layer_texture; m_layer_pass_desc.colorAttachments[0].texture = layer_texture;
m_layer_pass_desc.colorAttachments[0].loadAction = MTLLoadActionClear; m_layer_pass_desc.colorAttachments[0].loadAction = MTLLoadActionClear;
m_render_encoder = [[m_render_cmdbuf renderCommandEncoderWithDescriptor:m_layer_pass_desc] retain]; m_render_encoder = [[m_render_cmdbuf renderCommandEncoderWithDescriptor:m_layer_pass_desc] retain];
s_stats.num_render_passes++;
std::memset(m_current_render_targets.data(), 0, sizeof(m_current_render_targets)); std::memset(m_current_render_targets.data(), 0, sizeof(m_current_render_targets));
m_num_current_render_targets = 0; m_num_current_render_targets = 0;
m_current_depth_target = nullptr; m_current_depth_target = nullptr;

View file

@ -76,6 +76,8 @@ bool OpenGLDevice::DownloadTexture(GPUTexture* texture, u32 x, u32 y, u32 width,
const u32 layer = 0; const u32 layer = 0;
const u32 level = 0; const u32 level = 0;
s_stats.num_downloads++;
if (GLAD_GL_VERSION_4_5 || GLAD_GL_ARB_get_texture_sub_image) if (GLAD_GL_VERSION_4_5 || GLAD_GL_ARB_get_texture_sub_image)
{ {
glGetTextureSubImage(T->GetGLId(), level, x, y, layer, width, height, 1, gl_format, gl_type, glGetTextureSubImage(T->GetGLId(), level, x, y, layer, width, height, 1, gl_format, gl_type,
@ -116,6 +118,8 @@ void OpenGLDevice::CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32
CommitClear(D); CommitClear(D);
CommitClear(S); CommitClear(S);
s_stats.num_copies++;
const GLuint sid = S->GetGLId(); const GLuint sid = S->GetGLId();
const GLuint did = D->GetGLId(); const GLuint did = D->GetGLId();
if (GLAD_GL_VERSION_4_3 || GLAD_GL_ARB_copy_image) if (GLAD_GL_VERSION_4_3 || GLAD_GL_ARB_copy_image)
@ -192,6 +196,8 @@ void OpenGLDevice::ResolveTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u
CommitClear(D); CommitClear(D);
} }
s_stats.num_copies++;
glDisable(GL_SCISSOR_TEST); glDisable(GL_SCISSOR_TEST);
glBlitFramebuffer(src_x, src_y, src_x + width, src_y + height, dst_x, dst_y, dst_x + width, dst_y + height, glBlitFramebuffer(src_x, src_y, src_x + width, src_y + height, dst_x, dst_y, dst_x + width, dst_y + height,
GL_COLOR_BUFFER_BIT, GL_LINEAR); GL_COLOR_BUFFER_BIT, GL_LINEAR);
@ -307,7 +313,8 @@ bool OpenGLDevice::HasSurface() const
} }
bool OpenGLDevice::CreateDevice(const std::string_view& adapter, bool threaded_presentation, bool OpenGLDevice::CreateDevice(const std::string_view& adapter, bool threaded_presentation,
std::optional<bool> exclusive_fullscreen_control, FeatureMask disabled_features, Error* error) std::optional<bool> exclusive_fullscreen_control, FeatureMask disabled_features,
Error* error)
{ {
m_gl_context = GL::Context::Create(m_window_info, error); m_gl_context = GL::Context::Create(m_window_info, error);
if (!m_gl_context) if (!m_gl_context)
@ -1030,6 +1037,8 @@ ALWAYS_INLINE_RELEASE void OpenGLDevice::SetVertexBufferOffsets(u32 base_vertex)
void OpenGLDevice::Draw(u32 vertex_count, u32 base_vertex) void OpenGLDevice::Draw(u32 vertex_count, u32 base_vertex)
{ {
s_stats.num_draws++;
if (glDrawElementsBaseVertex) [[likely]] if (glDrawElementsBaseVertex) [[likely]]
{ {
glDrawArrays(m_current_pipeline->GetTopology(), base_vertex, vertex_count); glDrawArrays(m_current_pipeline->GetTopology(), base_vertex, vertex_count);
@ -1042,6 +1051,8 @@ void OpenGLDevice::Draw(u32 vertex_count, u32 base_vertex)
void OpenGLDevice::DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) void OpenGLDevice::DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex)
{ {
s_stats.num_draws++;
if (glDrawElementsBaseVertex) [[likely]] if (glDrawElementsBaseVertex) [[likely]]
{ {
const void* indices = reinterpret_cast<const void*>(static_cast<uintptr_t>(base_index) * sizeof(u16)); const void* indices = reinterpret_cast<const void*>(static_cast<uintptr_t>(base_index) * sizeof(u16));
@ -1066,7 +1077,9 @@ void OpenGLDevice::MapVertexBuffer(u32 vertex_size, u32 vertex_count, void** map
void OpenGLDevice::UnmapVertexBuffer(u32 vertex_size, u32 vertex_count) void OpenGLDevice::UnmapVertexBuffer(u32 vertex_size, u32 vertex_count)
{ {
m_vertex_buffer->Unmap(vertex_size * vertex_count); const u32 size = vertex_size * vertex_count;
s_stats.buffer_streamed += size;
m_vertex_buffer->Unmap(size);
} }
void OpenGLDevice::MapIndexBuffer(u32 index_count, DrawIndex** map_ptr, u32* map_space, u32* map_base_index) void OpenGLDevice::MapIndexBuffer(u32 index_count, DrawIndex** map_ptr, u32* map_space, u32* map_base_index)
@ -1079,7 +1092,9 @@ void OpenGLDevice::MapIndexBuffer(u32 index_count, DrawIndex** map_ptr, u32* map
void OpenGLDevice::UnmapIndexBuffer(u32 used_index_count) void OpenGLDevice::UnmapIndexBuffer(u32 used_index_count)
{ {
m_index_buffer->Unmap(sizeof(DrawIndex) * used_index_count); const u32 size = sizeof(DrawIndex) * used_index_count;
s_stats.buffer_streamed += size;
m_index_buffer->Unmap(size);
} }
void OpenGLDevice::PushUniformBuffer(const void* data, u32 data_size) void OpenGLDevice::PushUniformBuffer(const void* data, u32 data_size)
@ -1087,6 +1102,7 @@ void OpenGLDevice::PushUniformBuffer(const void* data, u32 data_size)
const auto res = m_uniform_buffer->Map(m_uniform_buffer_alignment, data_size); const auto res = m_uniform_buffer->Map(m_uniform_buffer_alignment, data_size);
std::memcpy(res.pointer, data, data_size); std::memcpy(res.pointer, data, data_size);
m_uniform_buffer->Unmap(data_size); m_uniform_buffer->Unmap(data_size);
s_stats.buffer_streamed += data_size;
glBindBufferRange(GL_UNIFORM_BUFFER, 1, m_uniform_buffer->GetGLBufferId(), res.buffer_offset, data_size); glBindBufferRange(GL_UNIFORM_BUFFER, 1, m_uniform_buffer->GetGLBufferId(), res.buffer_offset, data_size);
} }
@ -1099,6 +1115,7 @@ void* OpenGLDevice::MapUniformBuffer(u32 size)
void OpenGLDevice::UnmapUniformBuffer(u32 size) void OpenGLDevice::UnmapUniformBuffer(u32 size)
{ {
const u32 pos = m_uniform_buffer->Unmap(size); const u32 pos = m_uniform_buffer->Unmap(size);
s_stats.buffer_streamed += pos;
glBindBufferRange(GL_UNIFORM_BUFFER, 1, m_uniform_buffer->GetGLBufferId(), pos, size); glBindBufferRange(GL_UNIFORM_BUFFER, 1, m_uniform_buffer->GetGLBufferId(), pos, size);
} }
@ -1135,6 +1152,7 @@ void OpenGLDevice::SetRenderTargets(GPUTexture* const* rts, u32 num_rts, GPUText
} }
} }
s_stats.num_render_passes++;
m_current_fbo = fbo; m_current_fbo = fbo;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
} }

View file

@ -166,6 +166,9 @@ std::unique_ptr<OpenGLTexture> OpenGLTexture::Create(u32 width, u32 height, u32
const u32 alignment = ((data_pitch % 4) == 0) ? 4 : (((data_pitch % 2) == 0) ? 2 : 1); const u32 alignment = ((data_pitch % 4) == 0) ? 4 : (((data_pitch % 2) == 0) ? 2 : 1);
if (data) if (data)
{ {
GPUDevice::GetStatistics().buffer_streamed += data_pitch * height;
GPUDevice::GetStatistics().num_uploads++;
glPixelStorei(GL_UNPACK_ROW_LENGTH, data_pitch / pixel_size); glPixelStorei(GL_UNPACK_ROW_LENGTH, data_pitch / pixel_size);
if (alignment != 4) if (alignment != 4)
glPixelStorei(GL_UNPACK_ALIGNMENT, alignment); glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
@ -248,6 +251,9 @@ bool OpenGLTexture::Update(u32 x, u32 y, u32 width, u32 height, const void* data
CommitClear(); CommitClear();
GPUDevice::GetStatistics().buffer_streamed += map_size;
GPUDevice::GetStatistics().num_uploads++;
OpenGLDevice::BindUpdateTextureUnit(); OpenGLDevice::BindUpdateTextureUnit();
glBindTexture(target, m_id); glBindTexture(target, m_id);
@ -309,6 +315,10 @@ void OpenGLTexture::Unmap()
const u32 pitch = Common::AlignUpPow2(static_cast<u32>(m_map_width) * GetPixelSize(), TEXTURE_UPLOAD_PITCH_ALIGNMENT); const u32 pitch = Common::AlignUpPow2(static_cast<u32>(m_map_width) * GetPixelSize(), TEXTURE_UPLOAD_PITCH_ALIGNMENT);
const u32 upload_size = pitch * static_cast<u32>(m_map_height); const u32 upload_size = pitch * static_cast<u32>(m_map_height);
GPUDevice::GetStatistics().buffer_streamed += upload_size;
GPUDevice::GetStatistics().num_uploads++;
OpenGLStreamBuffer* sb = OpenGLDevice::GetTextureStreamBuffer(); OpenGLStreamBuffer* sb = OpenGLDevice::GetTextureStreamBuffer();
sb->Unmap(upload_size); sb->Unmap(upload_size);
sb->Bind(); sb->Bind();
@ -627,7 +637,10 @@ void* OpenGLTextureBuffer::Map(u32 required_elements)
void OpenGLTextureBuffer::Unmap(u32 used_elements) void OpenGLTextureBuffer::Unmap(u32 used_elements)
{ {
m_buffer->Unmap(used_elements * GetElementSize(m_format)); const u32 size = used_elements * GetElementSize(m_format);
GPUDevice::GetStatistics().buffer_streamed += size;
GPUDevice::GetStatistics().num_uploads++;
m_buffer->Unmap(size);
} }
void OpenGLTextureBuffer::SetDebugName(const std::string_view& name) void OpenGLTextureBuffer::SetDebugName(const std::string_view& name)

View file

@ -2568,6 +2568,8 @@ void VulkanDevice::CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32
if (InRenderPass()) if (InRenderPass())
EndRenderPass(); EndRenderPass();
s_stats.num_copies++;
S->SetUseFenceCounter(GetCurrentFenceCounter()); S->SetUseFenceCounter(GetCurrentFenceCounter());
D->SetUseFenceCounter(GetCurrentFenceCounter()); D->SetUseFenceCounter(GetCurrentFenceCounter());
S->TransitionToLayout((D == S) ? VulkanTexture::Layout::TransferSelf : VulkanTexture::Layout::TransferSrc); S->TransitionToLayout((D == S) ? VulkanTexture::Layout::TransferSelf : VulkanTexture::Layout::TransferSrc);
@ -2592,6 +2594,8 @@ void VulkanDevice::ResolveTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u
if (InRenderPass()) if (InRenderPass())
EndRenderPass(); EndRenderPass();
s_stats.num_copies++;
VulkanTexture* D = static_cast<VulkanTexture*>(dst); VulkanTexture* D = static_cast<VulkanTexture*>(dst);
VulkanTexture* S = static_cast<VulkanTexture*>(src); VulkanTexture* S = static_cast<VulkanTexture*>(src);
const VkCommandBuffer cmdbuf = GetCurrentCommandBuffer(); const VkCommandBuffer cmdbuf = GetCurrentCommandBuffer();
@ -2699,7 +2703,9 @@ void VulkanDevice::MapVertexBuffer(u32 vertex_size, u32 vertex_count, void** map
void VulkanDevice::UnmapVertexBuffer(u32 vertex_size, u32 vertex_count) void VulkanDevice::UnmapVertexBuffer(u32 vertex_size, u32 vertex_count)
{ {
m_vertex_buffer.CommitMemory(vertex_size * vertex_count); const u32 size = vertex_size * vertex_count;
s_stats.buffer_streamed += size;
m_vertex_buffer.CommitMemory(size);
} }
void VulkanDevice::MapIndexBuffer(u32 index_count, DrawIndex** map_ptr, u32* map_space, u32* map_base_index) void VulkanDevice::MapIndexBuffer(u32 index_count, DrawIndex** map_ptr, u32* map_space, u32* map_base_index)
@ -2719,12 +2725,15 @@ void VulkanDevice::MapIndexBuffer(u32 index_count, DrawIndex** map_ptr, u32* map
void VulkanDevice::UnmapIndexBuffer(u32 used_index_count) void VulkanDevice::UnmapIndexBuffer(u32 used_index_count)
{ {
m_index_buffer.CommitMemory(sizeof(DrawIndex) * used_index_count); const u32 size = sizeof(DrawIndex) * used_index_count;
s_stats.buffer_streamed += size;
m_index_buffer.CommitMemory(size);
} }
void VulkanDevice::PushUniformBuffer(const void* data, u32 data_size) void VulkanDevice::PushUniformBuffer(const void* data, u32 data_size)
{ {
DebugAssert(data_size < UNIFORM_PUSH_CONSTANTS_SIZE); DebugAssert(data_size < UNIFORM_PUSH_CONSTANTS_SIZE);
s_stats.buffer_streamed += data_size;
vkCmdPushConstants(GetCurrentCommandBuffer(), GetCurrentVkPipelineLayout(), UNIFORM_PUSH_CONSTANTS_STAGES, 0, vkCmdPushConstants(GetCurrentCommandBuffer(), GetCurrentVkPipelineLayout(), UNIFORM_PUSH_CONSTANTS_STAGES, 0,
data_size, data); data_size, data);
} }
@ -2745,6 +2754,7 @@ void* VulkanDevice::MapUniformBuffer(u32 size)
void VulkanDevice::UnmapUniformBuffer(u32 size) void VulkanDevice::UnmapUniformBuffer(u32 size)
{ {
s_stats.buffer_streamed += size;
m_uniform_buffer_position = m_uniform_buffer.GetCurrentOffset(); m_uniform_buffer_position = m_uniform_buffer.GetCurrentOffset();
m_uniform_buffer.CommitMemory(size); m_uniform_buffer.CommitMemory(size);
m_dirty_flags |= DIRTY_FLAG_DYNAMIC_OFFSETS; m_dirty_flags |= DIRTY_FLAG_DYNAMIC_OFFSETS;
@ -3147,6 +3157,8 @@ void VulkanDevice::BeginRenderPass()
vkCmdBeginRenderPass(GetCurrentCommandBuffer(), &bi, VK_SUBPASS_CONTENTS_INLINE); vkCmdBeginRenderPass(GetCurrentCommandBuffer(), &bi, VK_SUBPASS_CONTENTS_INLINE);
} }
s_stats.num_render_passes++;
// If this is a new command buffer, bind the pipeline and such. // If this is a new command buffer, bind the pipeline and such.
if (m_dirty_flags & DIRTY_FLAG_INITIAL) if (m_dirty_flags & DIRTY_FLAG_INITIAL)
SetInitialPipelineState(); SetInitialPipelineState();
@ -3212,6 +3224,7 @@ void VulkanDevice::BeginSwapChainRenderPass()
vkCmdBeginRenderPass(GetCurrentCommandBuffer(), &rp, VK_SUBPASS_CONTENTS_INLINE); vkCmdBeginRenderPass(GetCurrentCommandBuffer(), &rp, VK_SUBPASS_CONTENTS_INLINE);
} }
s_stats.num_render_passes++;
m_num_current_render_targets = 0; m_num_current_render_targets = 0;
std::memset(m_current_render_targets.data(), 0, sizeof(m_current_render_targets)); std::memset(m_current_render_targets.data(), 0, sizeof(m_current_render_targets));
m_current_depth_target = nullptr; m_current_depth_target = nullptr;
@ -3568,11 +3581,13 @@ bool VulkanDevice::UpdateDescriptorSets(u32 dirty)
void VulkanDevice::Draw(u32 vertex_count, u32 base_vertex) void VulkanDevice::Draw(u32 vertex_count, u32 base_vertex)
{ {
PreDrawCheck(); PreDrawCheck();
s_stats.num_draws++;
vkCmdDraw(GetCurrentCommandBuffer(), vertex_count, 1, base_vertex, 0); vkCmdDraw(GetCurrentCommandBuffer(), vertex_count, 1, base_vertex, 0);
} }
void VulkanDevice::DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) void VulkanDevice::DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex)
{ {
PreDrawCheck(); PreDrawCheck();
s_stats.num_draws++;
vkCmdDrawIndexed(GetCurrentCommandBuffer(), index_count, 1, base_index, base_vertex, 0); vkCmdDrawIndexed(GetCurrentCommandBuffer(), index_count, 1, base_index, base_vertex, 0);
} }

View file

@ -339,6 +339,9 @@ bool VulkanTexture::Update(u32 x, u32 y, u32 width, u32 height, const void* data
sbuffer.CommitMemory(required_size); sbuffer.CommitMemory(required_size);
} }
GPUDevice::GetStatistics().buffer_streamed += required_size;
GPUDevice::GetStatistics().num_uploads++;
const VkCommandBuffer cmdbuf = GetCommandBufferForUpdate(); const VkCommandBuffer cmdbuf = GetCommandBufferForUpdate();
// if we're an rt and have been cleared, and the full rect isn't being uploaded, do the clear // if we're an rt and have been cleared, and the full rect isn't being uploaded, do the clear
@ -407,6 +410,9 @@ void VulkanTexture::Unmap()
const u32 offset = sb.GetCurrentOffset(); const u32 offset = sb.GetCurrentOffset();
sb.CommitMemory(req_size); sb.CommitMemory(req_size);
GPUDevice::GetStatistics().buffer_streamed += req_size;
GPUDevice::GetStatistics().num_uploads++;
// first time the texture is used? don't leave it undefined // first time the texture is used? don't leave it undefined
const VkCommandBuffer cmdbuf = GetCommandBufferForUpdate(); const VkCommandBuffer cmdbuf = GetCommandBufferForUpdate();
if (m_layout == Layout::Undefined) if (m_layout == Layout::Undefined)
@ -745,6 +751,8 @@ bool VulkanDevice::DownloadTexture(GPUTexture* texture, u32 x, u32 y, u32 width,
return false; return false;
} }
s_stats.num_downloads++;
if (InRenderPass()) if (InRenderPass())
EndRenderPass(); EndRenderPass();
@ -1015,7 +1023,10 @@ void* VulkanTextureBuffer::Map(u32 required_elements)
void VulkanTextureBuffer::Unmap(u32 used_elements) void VulkanTextureBuffer::Unmap(u32 used_elements)
{ {
m_buffer.CommitMemory(GetElementSize(m_format) * used_elements); const u32 size = GetElementSize(m_format) * used_elements;
GPUDevice::GetStatistics().buffer_streamed += size;
GPUDevice::GetStatistics().num_uploads++;
m_buffer.CommitMemory(size);
} }
void VulkanTextureBuffer::SetDebugName(const std::string_view& name) void VulkanTextureBuffer::SetDebugName(const std::string_view& name)