feat: Remove outdated performance analysis documents and update optimization summaries for dungeon and overworld loading

This commit is contained in:
scawful
2025-10-04 03:05:33 -04:00
parent 2931634837
commit 06c613804e
4 changed files with 27 additions and 728 deletions

View File

@@ -14,217 +14,48 @@
### 1. **Performance Monitoring System with Feature Flag**
#### **Features Added**
- **Feature Flag Control**: `kEnablePerformanceMonitoring` in FeatureFlags
- **Zero-Overhead When Disabled**: ScopedTimer becomes no-op when monitoring is off
- **UI Toggle**: Performance monitoring can be enabled/disabled in Settings
#### **Implementation**
```cpp
// Feature flag integration
ScopedTimer::ScopedTimer(const std::string& operation_name)
: operation_name_(operation_name),
enabled_(core::FeatureFlags::get().kEnablePerformanceMonitoring) {
if (enabled_) {
PerformanceMonitor::Get().StartTimer(operation_name_);
}
}
```
- **Feature Flag Control**: `kEnablePerformanceMonitoring` in FeatureFlags allows enabling/disabling the system.
- **Zero-Overhead When Disabled**: `ScopedTimer` becomes a no-op when monitoring is off.
- **UI Toggle**: Performance monitoring can be toggled in the Settings UI.
### 2. **DungeonEditor Parallel Loading (79% Speedup)**
#### **Problem Solved**
- **DungeonEditor::LoadAllRooms**: 17,966ms → 3,746ms
- Loading 296 rooms sequentially was the primary bottleneck
#### **Solution: Multi-Threaded Room Loading**
```cpp
// Parallel processing with up to 8 threads
const int max_concurrency = std::min(8, std::thread::hardware_concurrency());
const int rooms_per_thread = (296 + max_concurrency - 1) / max_concurrency;
// Each thread processes ~37 rooms independently
for (int i = start_room; i < end_room; ++i) {
rooms[i] = zelda3::LoadRoomFromRom(rom_, i);
rooms[i].LoadObjects();
// ... other room processing
}
```
#### **Key Features**
- **Thread-Safe Result Collection**: Mutex-protected shared data structures
- **Hardware-Aware**: Automatically adapts to available CPU cores
- **Error Handling**: Proper status propagation per thread
- **Result Synchronization**: Main thread processes collected results
- **Problem Solved**: Loading 296 rooms sequentially was the primary bottleneck, taking ~18 seconds.
- **Solution**: Implemented multi-threaded room loading, using up to 8 threads to process rooms in parallel. This includes thread-safe collection of results and hardware-aware concurrency.
### 3. **Incremental Overworld Map Loading**
#### **Problem Solved**
- Blank maps visible during loading
- All maps loaded upfront causing UI blocking
#### **Solution: Priority-Based Incremental Loading**
```cpp
// Increased from 2 to 8 textures per frame
const int textures_per_frame = 8;
// Priority system: current world maps first
if (is_current_world || processed < textures_per_frame / 2) {
Renderer::Get().RenderBitmap(*it);
processed++;
}
```
#### **Key Features**
- **Priority Loading**: Current world maps load first
- **4x Faster Texture Creation**: 8 textures per frame vs 2
- **Loading Indicators**: "Loading..." placeholders for pending maps
- **Graceful Degradation**: Only draws maps with textures
- **Problem Solved**: UI would block and show blank maps while all 160 overworld maps were loaded upfront.
- **Solution**: Implemented a priority-based incremental loading system. It creates textures for the current world's maps first, at a 4x faster rate (8 per frame), while showing "Loading..." placeholders for the rest.
### 4. **On-Demand Map Reloading**
#### **Problem Solved**
- Full map refresh on every property change
- Expensive rebuilds for non-visible maps
- **Problem Solved**: Any property change would trigger an expensive full map refresh, even for non-visible maps.
- **Solution**: An intelligent refresh system now only reloads maps that are currently visible. Changes to non-visible maps are deferred until they are viewed.
#### **Solution: Intelligent Refresh System**
```cpp
void RefreshOverworldMapOnDemand(int map_index) {
// Only refresh visible maps immediately
bool is_current_map = (map_index == current_map_);
bool is_current_world = (map_index / 0x40 == current_world_);
if (!is_current_map && !is_current_world) {
// Defer refresh for non-visible maps
maps_bmp_[map_index].set_modified(true);
return;
}
// Immediate refresh for visible maps
RefreshChildMapOnDemand(map_index);
}
```
---
#### **Key Features**
- **Visibility-Aware**: Only refreshes visible maps immediately
- **Deferred Processing**: Non-visible maps marked for later refresh
- **Selective Updates**: Only rebuilds changed components
- **Smart Sibling Handling**: Large map siblings refreshed intelligently
## Appendix A: Dungeon Editor Parallel Optimization
## 🎯 **Technical Architecture**
- **Problem Identified**: `DungeonEditor::LoadAllRooms` took **17.97 seconds**, accounting for 99.9% of loading time.
- **Strategy**: The 296 independent rooms were loaded in parallel across up to 8 threads (~37 rooms per thread).
- **Implementation**: Used `std::async` to launch tasks and `std::mutex` to safely collect results (like room size and palette data). Results are sorted on the main thread for consistency.
- **Result**: Loading time for the dungeon editor was reduced by **79%** to ~3.7 seconds.
### **Performance Monitoring System**
```
FeatureFlags::kEnablePerformanceMonitoring
↓ (enabled/disabled)
ScopedTimer (no-op when disabled)
↓ (when enabled)
PerformanceMonitor::StartTimer/EndTimer
Operation timing collection
Performance summary output
```
---
### **Parallel Loading Architecture**
```
Main Thread
Spawn 8 Worker Threads
↓ (parallel)
Thread 1: Rooms 0-36 Thread 2: Rooms 37-73 ... Thread 8: Rooms 259-295
↓ (thread-safe collection)
Mutex-Protected Results
↓ (main thread)
Result Processing & Sorting
Map Population
```
## Appendix B: Overworld Load Optimization
### **Incremental Loading Flow**
```
ROM Load Start
Essential Maps (8 per world) → Immediate Texture Creation
Non-Essential Maps → Deferred Texture Creation
↓ (per frame)
ProcessDeferredTextures()
↓ (priority-based)
Current World Maps First → Other Maps
Loading Indicators for Pending Maps
```
- **Problem Identified**: `Overworld::Load` took **2.9 seconds**, with the main bottleneck being the sequential decompression of 160 map tiles (`DecompressAllMapTiles`).
- **Strategy**: Parallelize the decompression operations and implement lazy loading for maps that are not immediately visible.
- **Implementation**: The plan involves using `std::async` to decompress map batches concurrently and creating a system to only load essential maps on startup, deferring the rest to a background process.
- **Expected Result**: A 70-80% reduction in initial overworld loading time.
## 📈 **Performance Impact Analysis**
---
### **DungeonEditor Optimization**
- **Before**: 17,967ms (single-threaded)
- **After**: 3,747ms (8-threaded)
- **Speedup**: 4.8x theoretical, 4.0x actual (due to overhead)
- **Efficiency**: 83% of theoretical maximum
## Appendix C: Renderer Optimization
### **OverworldEditor Optimization**
- **Loading Time**: Reduced from blocking to progressive
- **Texture Creation**: 4x faster (8 vs 2 per frame)
- **User Experience**: No more blank maps, smooth loading
- **Memory Usage**: Reduced initial footprint
### **Overall System Impact**
- **Total Loading Time**: 18.6s → 4.7s (75% reduction)
- **UI Responsiveness**: Near-instant vs 18-second freeze
- **Memory Efficiency**: Reduced initial allocations
- **CPU Utilization**: Better multi-core usage
## 🔧 **Configuration Options**
### **Performance Monitoring**
```cpp
// Enable/disable in UI or code
FeatureFlags::get().kEnablePerformanceMonitoring = true/false;
// Zero overhead when disabled
ScopedTimer timer("Operation"); // No-op when monitoring disabled
```
### **Parallel Loading Tuning**
```cpp
// Adjust thread count based on system
constexpr int kMaxConcurrency = 8; // Reasonable default
const int max_concurrency = std::min(kMaxConcurrency,
std::thread::hardware_concurrency());
```
### **Incremental Loading Tuning**
```cpp
// Adjust textures per frame based on performance
const int textures_per_frame = 8; // Balance between speed and UI responsiveness
```
## 🎯 **Future Optimization Opportunities**
### **Potential Further Improvements**
1. **Memory-Mapped ROM Access**: Reduce memory copying during loading
2. **Background Thread Pool**: Reuse threads across operations
3. **Predictive Loading**: Load likely-to-be-accessed maps in advance
4. **Compression Caching**: Cache decompressed data for faster subsequent loads
5. **GPU-Accelerated Texture Creation**: Move texture creation to GPU
### **Monitoring and Profiling**
1. **Real-Time Performance Metrics**: In-app performance dashboard
2. **Memory Usage Tracking**: Monitor memory allocations during loading
3. **Thread Utilization Metrics**: Track CPU core usage efficiency
4. **User Interaction Timing**: Measure time to interactive
## ✅ **Success Metrics Achieved**
-**75% reduction** in total loading time (18.6s → 4.7s)
-**79% improvement** in DungeonEditor loading (17.9s → 3.7s)
-**Zero-overhead** performance monitoring when disabled
-**Smooth incremental loading** with visual feedback
-**Intelligent on-demand refreshing** for better responsiveness
-**Multi-threaded architecture** utilizing all CPU cores
-**Backward compatibility** maintained throughout
## 🚀 **Result: Lightning-Fast YAZE**
YAZE has been transformed from a slow-loading application with 18-second freezes to a **lightning-fast ROM editor** that loads in under 5 seconds with smooth, progressive loading and intelligent resource management. The optimizations provide both immediate performance gains and a foundation for future enhancements.
- **Problem Identified**: The original renderer created GPU textures synchronously on the main thread for all 160 overworld maps, blocking the UI for several seconds.
- **Strategy**: Defer texture creation. Bitmaps and surface data are prepared first (a CPU-bound task that can be backgrounded), while the actual GPU texture creation (a main-thread-only task) is done progressively or on-demand.
- **Implementation**: A `CreateBitmapWithoutTexture` method was introduced. A lazy loading system (`ProcessDeferredTextures`) processes a few textures per frame to avoid blocking, and `EnsureMapTexture` creates a texture immediately if a map becomes visible.
- **Result**: A much more responsive UI during ROM loading, with an initial load time of only ~200-500ms.