Files
yaze/src/app/gfx/performance_dashboard.cc
scawful bac03ee08e Refactor Performance Monitoring System for Unified Functionality
- Merged the old PerformanceMonitor interface into gfx::PerformanceProfiler, providing a unified performance monitoring system.
- Updated header files to maintain backward compatibility with aliases for PerformanceMonitor and ScopedTimer.
- Removed legacy timer methods and integrated memory pool for efficient data handling in performance profiling.
- Enhanced PerformanceDashboard to utilize the new profiler, including controls for enabling monitoring and clearing data.
- Improved performance reporting with detailed statistics and memory usage insights.
2025-09-29 19:21:31 -04:00

537 lines
18 KiB
C++

#include "app/gfx/performance_dashboard.h"
#include <algorithm>
#include <iomanip>
#include <sstream>
#include "app/gfx/atlas_renderer.h"
#include "app/gfx/memory_pool.h"
#include "app/gfx/performance_profiler.h"
#include "imgui/imgui.h"
namespace yaze {
namespace gfx {
PerformanceDashboard& PerformanceDashboard::Get() {
static PerformanceDashboard instance;
return instance;
}
void PerformanceDashboard::Initialize() {
visible_ = false;
last_update_time_ = std::chrono::high_resolution_clock::now();
frame_time_history_.reserve(kHistorySize);
memory_usage_history_.reserve(kHistorySize);
}
void PerformanceDashboard::Update() {
auto now = std::chrono::high_resolution_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
now - last_update_time_);
if (elapsed.count() >= kUpdateIntervalMs) {
CollectMetrics();
UpdateOptimizationStatus();
AnalyzePerformance();
last_update_time_ = now;
}
}
void PerformanceDashboard::Render() {
if (!visible_) {
return;
}
ImGui::Begin("Graphics Performance Dashboard", &visible_);
RenderMetricsPanel();
ImGui::Separator();
RenderOptimizationStatus();
ImGui::Separator();
RenderMemoryUsage();
ImGui::Separator();
RenderFrameRateGraph();
ImGui::Separator();
RenderRecommendations();
ImGui::End();
}
PerformanceSummary PerformanceDashboard::GetSummary() const {
PerformanceSummary summary;
summary.average_frame_time_ms = CalculateAverage(frame_time_history_);
summary.memory_usage_mb = current_metrics_.memory_usage_mb;
summary.cache_hit_ratio = current_metrics_.cache_hit_ratio;
// Calculate optimization score (0-100)
int score = 0;
if (optimization_status_.palette_lookup_optimized)
score += 20;
if (optimization_status_.dirty_region_tracking_enabled)
score += 20;
if (optimization_status_.resource_pooling_active)
score += 15;
if (optimization_status_.batch_operations_enabled)
score += 15;
if (optimization_status_.atlas_rendering_enabled)
score += 15;
if (optimization_status_.memory_pool_active)
score += 15;
summary.optimization_score = score;
// Generate status message
if (score >= 90) {
summary.status_message = "Excellent - All optimizations active";
} else if (score >= 70) {
summary.status_message = "Good - Most optimizations active";
} else if (score >= 50) {
summary.status_message = "Fair - Some optimizations active";
} else {
summary.status_message = "Poor - Few optimizations active";
}
// Generate recommendations
if (!optimization_status_.palette_lookup_optimized) {
summary.recommendations.push_back("Enable palette lookup optimization");
}
if (!optimization_status_.dirty_region_tracking_enabled) {
summary.recommendations.push_back("Enable dirty region tracking");
}
if (!optimization_status_.resource_pooling_active) {
summary.recommendations.push_back("Enable resource pooling");
}
if (!optimization_status_.batch_operations_enabled) {
summary.recommendations.push_back("Enable batch operations");
}
if (!optimization_status_.atlas_rendering_enabled) {
summary.recommendations.push_back("Enable atlas rendering");
}
if (!optimization_status_.memory_pool_active) {
summary.recommendations.push_back("Enable memory pool allocator");
}
return summary;
}
std::string PerformanceDashboard::ExportReport() const {
std::ostringstream report;
report << "=== YAZE Graphics Performance Report ===\n";
report << "Generated: "
<< std::chrono::system_clock::now().time_since_epoch().count()
<< "\n\n";
// Current metrics
report << "Current Performance Metrics:\n";
report << " Frame Time: " << std::fixed << std::setprecision(2)
<< current_metrics_.frame_time_ms << " ms\n";
report << " Palette Lookup: "
<< FormatTime(current_metrics_.palette_lookup_time_us) << "\n";
report << " Texture Updates: "
<< FormatTime(current_metrics_.texture_update_time_us) << "\n";
report << " Batch Operations: "
<< FormatTime(current_metrics_.batch_operation_time_us) << "\n";
report << " Memory Usage: " << std::fixed << std::setprecision(2)
<< current_metrics_.memory_usage_mb << " MB\n";
report << " Cache Hit Ratio: " << std::fixed << std::setprecision(1)
<< current_metrics_.cache_hit_ratio * 100.0 << "%\n";
report << " Draw Calls/Frame: " << current_metrics_.draw_calls_per_frame
<< "\n";
report << " Texture Updates/Frame: "
<< current_metrics_.texture_updates_per_frame << "\n\n";
// Optimization status
report << "Optimization Status:\n";
report << " Palette Lookup: "
<< (optimization_status_.palette_lookup_optimized ? "" : "") << "\n";
report << " Dirty Region Tracking: "
<< (optimization_status_.dirty_region_tracking_enabled ? "" : "")
<< "\n";
report << " Resource Pooling: "
<< (optimization_status_.resource_pooling_active ? "" : "") << "\n";
report << " Batch Operations: "
<< (optimization_status_.batch_operations_enabled ? "" : "") << "\n";
report << " Atlas Rendering: "
<< (optimization_status_.atlas_rendering_enabled ? "" : "") << "\n";
report << " Memory Pool: "
<< (optimization_status_.memory_pool_active ? "" : "") << "\n\n";
// Performance analysis
auto summary = GetSummary();
report << "Performance Summary:\n";
report << " Optimization Score: " << summary.optimization_score << "/100\n";
report << " Status: " << summary.status_message << "\n";
if (!summary.recommendations.empty()) {
report << "\nRecommendations:\n";
for (const auto& rec : summary.recommendations) {
report << " - " << rec << "\n";
}
}
return report.str();
}
void PerformanceDashboard::RenderMetricsPanel() const {
ImGui::Text("Performance Metrics");
ImGui::Columns(2, "MetricsColumns");
ImGui::Text("Frame Time: %.2f ms", current_metrics_.frame_time_ms);
ImGui::Text("Palette Lookup: %s",
FormatTime(current_metrics_.palette_lookup_time_us).c_str());
ImGui::Text("Texture Updates: %s",
FormatTime(current_metrics_.texture_update_time_us).c_str());
ImGui::Text("Batch Operations: %s",
FormatTime(current_metrics_.batch_operation_time_us).c_str());
ImGui::NextColumn();
ImGui::Text("Memory Usage: %.2f MB", current_metrics_.memory_usage_mb);
ImGui::Text("Cache Hit Ratio: %.1f%%",
current_metrics_.cache_hit_ratio * 100.0);
ImGui::Text("Draw Calls/Frame: %d", current_metrics_.draw_calls_per_frame);
ImGui::Text("Texture Updates/Frame: %d",
current_metrics_.texture_updates_per_frame);
ImGui::Columns(1);
}
void PerformanceDashboard::RenderOptimizationStatus() const {
ImGui::Text("Optimization Status");
ImGui::Columns(2, "OptimizationColumns");
ImGui::Text("Palette Lookup: %s",
optimization_status_.palette_lookup_optimized
? "✓ Optimized"
: "✗ Not Optimized");
ImGui::Text("Dirty Regions: %s",
optimization_status_.dirty_region_tracking_enabled
? "✓ Enabled"
: "✗ Disabled");
ImGui::Text(
"Resource Pooling: %s",
optimization_status_.resource_pooling_active ? "✓ Active" : "✗ Inactive");
ImGui::NextColumn();
ImGui::Text("Batch Operations: %s",
optimization_status_.batch_operations_enabled ? "✓ Enabled"
: "✗ Disabled");
ImGui::Text("Atlas Rendering: %s",
optimization_status_.atlas_rendering_enabled ? "✓ Enabled"
: "✗ Disabled");
ImGui::Text("Memory Pool: %s", optimization_status_.memory_pool_active
? "✓ Active"
: "✗ Inactive");
ImGui::Columns(1);
// Optimization score
auto summary = GetSummary();
ImGui::Text("Optimization Score: %d/100", summary.optimization_score);
// Progress bar
float progress = summary.optimization_score / 100.0F;
ImGui::ProgressBar(progress, ImVec2(-1, 0), summary.status_message.c_str());
}
void PerformanceDashboard::RenderMemoryUsage() {
ImGui::Text("Memory Usage");
// Memory usage graph
if (!memory_usage_history_.empty()) {
// Convert double vector to float vector for ImGui
std::vector<float> float_history;
float_history.reserve(memory_usage_history_.size());
for (double value : memory_usage_history_) {
float_history.push_back(static_cast<float>(value));
}
ImGui::PlotLines("Memory (MB)", float_history.data(),
static_cast<int>(float_history.size()));
}
// Memory pool stats
auto [used_bytes, total_bytes] = MemoryPool::Get().GetMemoryStats();
ImGui::Text("Memory Pool: %s / %s", FormatMemory(used_bytes).c_str(),
FormatMemory(total_bytes).c_str());
float pool_usage =
total_bytes > 0 ? static_cast<float>(used_bytes) / total_bytes : 0.0F;
ImGui::ProgressBar(pool_usage, ImVec2(-1, 0), "Memory Pool Usage");
// Atlas renderer stats
auto atlas_stats = AtlasRenderer::Get().GetStats();
ImGui::Text("Atlas Renderer: %d atlases, %d/%d entries used",
atlas_stats.total_atlases, atlas_stats.used_entries, atlas_stats.total_entries);
ImGui::Text("Atlas Memory: %s", FormatMemory(atlas_stats.total_memory).c_str());
if (atlas_stats.total_entries > 0) {
float atlas_usage = static_cast<float>(atlas_stats.used_entries) / atlas_stats.total_entries;
ImGui::ProgressBar(atlas_usage, ImVec2(-1, 0), "Atlas Utilization");
}
}
void PerformanceDashboard::RenderFrameRateGraph() {
ImGui::Text("Frame Rate Analysis");
if (!frame_time_history_.empty()) {
// Convert frame times to FPS
std::vector<float> fps_history;
fps_history.reserve(frame_time_history_.size());
for (double frame_time : frame_time_history_) {
if (frame_time > 0.0) {
fps_history.push_back(1000.0F / static_cast<float>(frame_time));
}
}
if (!fps_history.empty()) {
ImGui::PlotLines("FPS", fps_history.data(),
static_cast<int>(fps_history.size()));
}
}
// Frame time statistics
if (!frame_time_history_.empty()) {
double avg_frame_time = CalculateAverage(frame_time_history_);
double p95_frame_time = CalculatePercentile(frame_time_history_, 95.0);
double p99_frame_time = CalculatePercentile(frame_time_history_, 99.0);
ImGui::Text("Average Frame Time: %.2f ms", avg_frame_time);
ImGui::Text("95th Percentile: %.2f ms", p95_frame_time);
ImGui::Text("99th Percentile: %.2f ms", p99_frame_time);
}
}
void PerformanceDashboard::RenderRecommendations() const {
ImGui::Text("Performance Recommendations");
auto summary = GetSummary();
if (summary.recommendations.empty()) {
ImGui::TextColored(ImVec4(0, 1, 0, 1), "✓ All optimizations are active!");
} else {
ImGui::TextColored(ImVec4(1, 1, 0, 1),
"⚠ Performance improvements available:");
for (const auto& rec : summary.recommendations) {
ImGui::BulletText("%s", rec.c_str());
}
}
// Performance monitoring controls
static bool monitoring_enabled = PerformanceProfiler::IsEnabled();
if (ImGui::Checkbox("Enable Performance Monitoring", &monitoring_enabled)) {
PerformanceProfiler::SetEnabled(monitoring_enabled);
}
ImGui::SameLine();
if (ImGui::Button("Clear All Data")) {
PerformanceProfiler::Get().Clear();
}
ImGui::SameLine();
if (ImGui::Button("Generate Report")) {
std::string report = PerformanceProfiler::Get().GenerateReport(true);
}
// Export button
if (ImGui::Button("Export Performance Report")) {
std::string report = ExportReport();
// In a real implementation, you'd save this to a file
ImGui::SetClipboardText(report.c_str());
ImGui::Text("Report copied to clipboard");
}
}
void PerformanceDashboard::CollectMetrics() {
// Collect metrics from unified performance profiler
auto profiler = PerformanceProfiler::Get();
// Frame time (simplified - in real implementation, measure actual frame time)
if (!frame_time_history_.empty()) {
current_metrics_.frame_time_ms = frame_time_history_.back();
}
// Operation timings from various categories
auto palette_stats = profiler.GetStats("palette_lookup_optimized");
current_metrics_.palette_lookup_time_us = palette_stats.avg_time_us;
auto texture_stats = profiler.GetStats("texture_update_optimized");
current_metrics_.texture_update_time_us = texture_stats.avg_time_us;
auto batch_stats = profiler.GetStats("texture_batch_queue");
current_metrics_.batch_operation_time_us = batch_stats.avg_time_us;
// Memory usage from memory pool
auto [used_bytes, total_bytes] = MemoryPool::Get().GetMemoryStats();
current_metrics_.memory_usage_mb = used_bytes / (1024.0 * 1024.0);
// Calculate cache hit ratio based on actual performance data
double total_cache_operations = 0.0;
double total_cache_time = 0.0;
// Look for cache-related operations
for (const auto& op_name : profiler.GetOperationNames()) {
if (op_name.find("cache") != std::string::npos ||
op_name.find("tile_cache") != std::string::npos) {
auto stats = profiler.GetStats(op_name);
total_cache_operations += stats.sample_count;
total_cache_time += stats.total_time_ms;
}
}
// Estimate cache hit ratio based on operation speed
if (total_cache_operations > 0) {
double avg_cache_time = total_cache_time / total_cache_operations;
// Assume cache hits are < 10μs, misses are > 50μs
current_metrics_.cache_hit_ratio = std::max(0.0, std::min(1.0,
1.0 - (avg_cache_time - 10.0) / 40.0));
} else {
current_metrics_.cache_hit_ratio = 0.85; // Default estimate
}
// Count draw calls and texture updates from profiler data
int draw_calls = 0;
int texture_updates = 0;
for (const auto& op_name : profiler.GetOperationNames()) {
if (op_name.find("draw") != std::string::npos ||
op_name.find("render") != std::string::npos) {
draw_calls += profiler.GetOperationCount(op_name);
}
if (op_name.find("texture_update") != std::string::npos ||
op_name.find("texture") != std::string::npos) {
texture_updates += profiler.GetOperationCount(op_name);
}
}
current_metrics_.draw_calls_per_frame = draw_calls;
current_metrics_.texture_updates_per_frame = texture_updates;
// Update history
frame_time_history_.push_back(current_metrics_.frame_time_ms);
memory_usage_history_.push_back(current_metrics_.memory_usage_mb);
if (frame_time_history_.size() > kHistorySize) {
frame_time_history_.erase(frame_time_history_.begin());
}
if (memory_usage_history_.size() > kHistorySize) {
memory_usage_history_.erase(memory_usage_history_.begin());
}
}
void PerformanceDashboard::UpdateOptimizationStatus() {
auto profiler = PerformanceProfiler::Get();
auto [used_bytes, total_bytes] = MemoryPool::Get().GetMemoryStats();
// Check optimization status based on actual performance data
optimization_status_.palette_lookup_optimized = false;
optimization_status_.dirty_region_tracking_enabled = false;
optimization_status_.resource_pooling_active = (total_bytes > 0);
optimization_status_.batch_operations_enabled = false;
optimization_status_.atlas_rendering_enabled = true; // AtlasRenderer is implemented
optimization_status_.memory_pool_active = (total_bytes > 0);
// Analyze palette lookup performance
auto palette_stats = profiler.GetStats("palette_lookup_optimized");
if (palette_stats.avg_time_us > 0 && palette_stats.avg_time_us < 5.0) {
optimization_status_.palette_lookup_optimized = true;
}
// Analyze texture update performance
auto texture_stats = profiler.GetStats("texture_update_optimized");
if (texture_stats.avg_time_us > 0 && texture_stats.avg_time_us < 200.0) {
optimization_status_.dirty_region_tracking_enabled = true;
}
// Check for batch operations
auto batch_stats = profiler.GetStats("texture_batch_queue");
if (batch_stats.sample_count > 0) {
optimization_status_.batch_operations_enabled = true;
}
}
void PerformanceDashboard::AnalyzePerformance() {
// Compare with previous metrics to detect regressions
if (previous_metrics_.frame_time_ms > 0.0) {
double frame_time_change =
current_metrics_.frame_time_ms - previous_metrics_.frame_time_ms;
if (frame_time_change > 2.0) { // 2ms increase
// Performance regression detected
}
}
previous_metrics_ = current_metrics_;
}
double PerformanceDashboard::CalculateAverage(
const std::vector<double>& values) {
if (values.empty())
return 0.0;
double sum = 0.0;
for (double value : values) {
sum += value;
}
return sum / values.size();
}
double PerformanceDashboard::CalculatePercentile(
const std::vector<double>& values, double percentile) {
if (values.empty())
return 0.0;
std::vector<double> sorted_values = values;
std::sort(sorted_values.begin(), sorted_values.end());
size_t index =
static_cast<size_t>((percentile / 100.0) * sorted_values.size());
if (index >= sorted_values.size()) {
index = sorted_values.size() - 1;
}
return sorted_values[index];
}
std::string PerformanceDashboard::FormatTime(double time_us) {
if (time_us < 1.0) {
return std::to_string(static_cast<int>(time_us * 1000.0)) + " ns";
}
if (time_us < 1000.0) {
return std::to_string(static_cast<int>(time_us)) + " μs";
}
return std::to_string(static_cast<int>(time_us / 1000.0)) + " ms";
}
std::string PerformanceDashboard::FormatMemory(size_t bytes) {
if (bytes < 1024) {
return std::to_string(bytes) + " B";
}
if (bytes < 1024 * 1024) {
return std::to_string(bytes / 1024) + " KB";
}
return std::to_string(bytes / (1024 * 1024)) + " MB";
}
std::string PerformanceDashboard::GetOptimizationRecommendation() const {
auto summary = GetSummary();
if (summary.optimization_score >= 90) {
return "Performance is excellent. All optimizations are active.";
}
if (summary.optimization_score >= 70) {
return "Performance is good. Consider enabling remaining optimizations.";
}
if (summary.optimization_score >= 50) {
return "Performance is fair. Several optimizations are available.";
}
return "Performance needs improvement. Enable graphics optimizations.";
}
} // namespace gfx
} // namespace yaze