yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
graphics_optimizer.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <iomanip>
5#include <iostream>
6#include <sstream>
7
9
10namespace yaze {
11namespace gfx {
12
14 static GraphicsOptimizer instance;
15 return instance;
16}
17
26
28 const std::vector<uint8_t>& sheet_data, int sheet_id,
29 const SnesPalette& palette, OptimizationStrategy strategy) {
30 ScopedTimer timer("graphics_optimize_sheet");
31
32 OptimizationResult result;
33
34 try {
35 // Analyze the sheet
36 SheetOptimizationData data = AnalyzeSheet(sheet_data, sheet_id, palette);
37
38 if (!data.is_convertible) {
39 result.success = false;
40 result.message = "Sheet is not suitable for optimization";
41 return result;
42 }
43
44 // Check if optimization meets criteria
45 if (!ShouldOptimize(data, strategy)) {
46 result.success = false;
47 result.message = "Optimization does not meet criteria";
48 return result;
49 }
50
51 // Calculate potential savings
52 result.memory_saved = data.current_size - data.optimized_size;
53 result.performance_gain =
56 data.current_format, data.recommended_format, sheet_data);
57
58 // Check if optimization is worthwhile
59 if (result.memory_saved < min_memory_savings_ &&
62 result.success = false;
63 result.message = "Optimization benefits do not justify quality loss";
64 return result;
65 }
66
67 result.success = true;
68 result.message = "Optimization recommended";
69 result.recommended_formats.push_back(data.recommended_format);
70 result.sheet_recommendations[sheet_id] = data.recommended_format;
71
72 UpdateOptimizationStats("sheets_optimized", 1.0);
73 UpdateOptimizationStats("memory_saved",
74 static_cast<double>(result.memory_saved));
75 UpdateOptimizationStats("performance_gain",
76 static_cast<double>(result.performance_gain));
77
78 } catch (const std::exception& e) {
79 result.success = false;
80 result.message = "Optimization failed: " + std::string(e.what());
81 SDL_Log("GraphicsOptimizer::OptimizeSheet failed: %s", e.what());
82 }
83
84 return result;
85}
86
88 const std::unordered_map<int, std::vector<uint8_t>>& sheets,
89 const std::unordered_map<int, SnesPalette>& palettes,
90 OptimizationStrategy strategy) {
91 ScopedTimer timer("graphics_optimize_sheets");
92
93 OptimizationResult result;
94 result.success = true;
95
96 size_t total_memory_saved = 0;
97 float total_performance_gain = 0.0f;
98 float total_quality_loss = 0.0f;
99 int optimized_sheets = 0;
100
101 for (const auto& [sheet_id, sheet_data] : sheets) {
102 auto palette_it = palettes.find(sheet_id);
103 if (palette_it == palettes.end()) {
104 continue; // Skip sheets without palettes
105 }
106
107 auto sheet_result =
108 OptimizeSheet(sheet_data, sheet_id, palette_it->second, strategy);
109
110 if (sheet_result.success) {
111 total_memory_saved += sheet_result.memory_saved;
112 total_performance_gain += sheet_result.performance_gain;
113 total_quality_loss += sheet_result.quality_loss;
114 optimized_sheets++;
115
116 // Merge recommendations
117 result.recommended_formats.insert(
118 result.recommended_formats.end(),
119 sheet_result.recommended_formats.begin(),
120 sheet_result.recommended_formats.end());
121 result.sheet_recommendations.insert(
122 sheet_result.sheet_recommendations.begin(),
123 sheet_result.sheet_recommendations.end());
124 }
125 }
126
127 result.memory_saved = total_memory_saved;
128 result.performance_gain =
129 optimized_sheets > 0 ? total_performance_gain / optimized_sheets : 0.0f;
130 result.quality_loss =
131 optimized_sheets > 0 ? total_quality_loss / optimized_sheets : 0.0f;
132
133 if (optimized_sheets > 0) {
134 result.message =
135 "Optimized " + std::to_string(optimized_sheets) + " sheets";
136 } else {
137 result.success = false;
138 result.message = "No sheets could be optimized";
139 }
140
141 UpdateOptimizationStats("batch_optimizations", 1.0);
142 UpdateOptimizationStats("total_sheets_processed",
143 static_cast<double>(sheets.size()));
144
145 return result;
146}
147
149 const std::vector<uint8_t>& sheet_data, int sheet_id,
150 const SnesPalette& palette) {
151 // Check cache first
152 std::string cache_key = GenerateCacheKey(sheet_data, sheet_id);
153 auto cache_it = optimization_cache_.find(cache_key);
154 if (cache_it != optimization_cache_.end()) {
155 return cache_it->second;
156 }
157
158 ScopedTimer timer("graphics_analyze_sheet");
159
161 data.sheet_id = sheet_id;
162 data.current_size = sheet_data.size();
163
164 // Detect current format
166 sheet_data, 128, 32); // Standard sheet size
167
168 // Analyze color usage
169 data.colors_used = CountUsedColors(sheet_data, palette);
170
171 // Determine optimal format
173 sheet_data, palette, OptimizationStrategy::kBalanced);
174
175 // Calculate potential savings
176 const auto& current_info =
178 const auto& recommended_info =
180
181 data.optimized_size = (sheet_data.size() * recommended_info.bits_per_pixel) /
182 current_info.bits_per_pixel;
183 data.compression_ratio =
184 static_cast<float>(data.current_size) / data.optimized_size;
185
186 // Determine if conversion is beneficial
187 data.is_convertible =
188 (data.current_format != data.recommended_format) &&
189 (data.colors_used <= recommended_info.max_colors) &&
190 (data.compression_ratio > 1.1f); // At least 10% savings
191
193
194 // Cache the result
195 optimization_cache_[cache_key] = data;
196
197 return data;
198}
199
200std::unordered_map<int, SheetOptimizationData>
202 const std::unordered_map<int, std::vector<uint8_t>>& sheets,
203 const std::unordered_map<int, SnesPalette>& palettes) {
204 std::unordered_map<int, SheetOptimizationData> recommendations;
205
206 for (const auto& [sheet_id, sheet_data] : sheets) {
207 auto palette_it = palettes.find(sheet_id);
208 if (palette_it == palettes.end()) {
209 continue;
210 }
211
212 recommendations[sheet_id] =
213 AnalyzeSheet(sheet_data, sheet_id, palette_it->second);
214 }
215
216 return recommendations;
217}
218
220 const std::unordered_map<int, SheetOptimizationData>& recommendations,
221 std::unordered_map<int, std::vector<uint8_t>>& sheets,
222 std::unordered_map<int, SnesPalette>& palettes) {
223 ScopedTimer timer("graphics_apply_optimizations");
224
225 OptimizationResult result;
226 result.success = true;
227
228 size_t total_memory_saved = 0;
229 int optimized_sheets = 0;
230
231 for (const auto& [sheet_id, data] : recommendations) {
232 if (!data.is_convertible) {
233 continue;
234 }
235
236 auto sheet_it = sheets.find(sheet_id);
237 if (sheet_it == sheets.end()) {
238 continue;
239 }
240
241 try {
242 // Convert the sheet data
243 auto converted_data = BppFormatManager::Get().ConvertFormat(
244 sheet_it->second, data.current_format, data.recommended_format, 128,
245 32);
246
247 // Update the sheet
248 sheet_it->second = converted_data;
249
250 // Optimize palette if needed
251 auto palette_it = palettes.find(sheet_id);
252 if (palette_it != palettes.end()) {
253 std::vector<int> used_colors;
254 for (int i = 0; i < data.colors_used; ++i) {
255 used_colors.push_back(i);
256 }
257
258 palette_it->second = BppFormatManager::Get().OptimizePaletteForFormat(
259 palette_it->second, data.recommended_format, used_colors);
260 }
261
262 total_memory_saved += data.current_size - data.optimized_size;
263 optimized_sheets++;
264
265 result.sheet_recommendations[sheet_id] = data.recommended_format;
266
267 } catch (const std::exception& e) {
268 SDL_Log("Failed to optimize sheet %d: %s", sheet_id, e.what());
269 }
270 }
271
272 result.memory_saved = total_memory_saved;
273 result.message = "Optimized " + std::to_string(optimized_sheets) + " sheets";
274
275 UpdateOptimizationStats("optimizations_applied",
276 static_cast<double>(optimized_sheets));
277 UpdateOptimizationStats("total_memory_saved",
278 static_cast<double>(total_memory_saved));
279
280 return result;
281}
282
283std::unordered_map<std::string, double>
287
292
294 size_t min_memory_savings,
295 float performance_threshold) {
296 max_quality_loss_ = max_quality_loss;
297 min_memory_savings_ = min_memory_savings;
298 performance_threshold_ = performance_threshold;
299}
300
301// Helper method implementations
302
304 const std::vector<uint8_t>& data, const SnesPalette& palette,
305 OptimizationStrategy strategy) {
306 int colors_used = CountUsedColors(data, palette);
307
308 // Determine optimal format based on color usage and strategy
309 switch (strategy) {
311 if (colors_used <= 4)
312 return BppFormat::kBpp2;
313 if (colors_used <= 8)
314 return BppFormat::kBpp3;
315 if (colors_used <= 16)
316 return BppFormat::kBpp4;
317 break;
318
320 // Prefer formats that work well with atlas rendering
321 if (colors_used <= 16)
322 return BppFormat::kBpp4;
323 break;
324
326 // Only optimize if significant memory savings
327 if (colors_used <= 4)
328 return BppFormat::kBpp2;
329 break;
330
332 if (colors_used <= 4)
333 return BppFormat::kBpp2;
334 if (colors_used <= 8)
335 return BppFormat::kBpp3;
336 if (colors_used <= 16)
337 return BppFormat::kBpp4;
338 break;
339 }
340
341 return BppFormat::kBpp8; // Default to 8BPP
342}
343
345 BppFormat from_format, BppFormat to_format,
346 const std::vector<uint8_t>& data) {
347 if (from_format == to_format)
348 return 0.0f;
349
350 // Higher BPP to lower BPP conversions may lose quality
351 if (static_cast<int>(from_format) > static_cast<int>(to_format)) {
352 int bpp_diff = static_cast<int>(from_format) - static_cast<int>(to_format);
353 return std::min(
354 1.0f, static_cast<float>(bpp_diff) * 0.1f); // 10% loss per BPP level
355 }
356
357 return 0.0f; // Lower to higher BPP is lossless
358}
359
361 BppFormat from_format, BppFormat to_format,
362 const std::vector<uint8_t>& data) {
363 if (from_format == to_format)
364 return 0;
365
366 const auto& from_info = BppFormatManager::Get().GetFormatInfo(from_format);
367 const auto& to_info = BppFormatManager::Get().GetFormatInfo(to_format);
368
369 size_t from_size = data.size();
370 size_t to_size =
371 (from_size * to_info.bits_per_pixel) / from_info.bits_per_pixel;
372
373 return from_size - to_size;
374}
375
377 BppFormat to_format) {
378 if (from_format == to_format)
379 return 0.0f;
380
381 // Lower BPP formats generally render faster
382 if (static_cast<int>(from_format) > static_cast<int>(to_format)) {
383 int bpp_diff = static_cast<int>(from_format) - static_cast<int>(to_format);
384 return std::min(
385 0.5f, static_cast<float>(bpp_diff) * 0.1f); // 10% gain per BPP level
386 }
387
388 return 0.0f;
389}
390
392 OptimizationStrategy strategy) {
393 if (!data.is_convertible)
394 return false;
395
396 switch (strategy) {
398 return data.compression_ratio > 1.2f; // At least 20% savings
399
401 return data.compression_ratio > 1.1f; // At least 10% savings
402
404 return data.compression_ratio > 1.5f; // At least 50% savings
405
407 return data.compression_ratio > 1.15f; // At least 15% savings
408 }
409
410 return false;
411}
412
414 const SheetOptimizationData& data) {
415 std::ostringstream reason;
416
417 reason << "Convert from "
419 << " to "
421 << " (uses " << data.colors_used << " colors, " << std::fixed
422 << std::setprecision(1) << (data.compression_ratio - 1.0f) * 100.0f
423 << "% memory savings)";
424
425 return reason.str();
426}
427
428int GraphicsOptimizer::CountUsedColors(const std::vector<uint8_t>& data,
429 const SnesPalette& palette) {
430 std::vector<bool> used_colors(palette.size(), false);
431
432 for (uint8_t pixel : data) {
433 if (pixel < palette.size()) {
434 used_colors[pixel] = true;
435 }
436 }
437
438 int count = 0;
439 for (bool used : used_colors) {
440 if (used)
441 count++;
442 }
443
444 return count;
445}
446
448 const std::vector<uint8_t>& data, const SnesPalette& palette) {
449 int used_colors = CountUsedColors(data, palette);
450 return static_cast<float>(used_colors) / palette.size();
451}
452
454 const std::vector<uint8_t>& data) {
455 std::vector<int> distribution(256, 0);
456
457 for (uint8_t pixel : data) {
458 distribution[pixel]++;
459 }
460
461 return distribution;
462}
463
465 const std::vector<uint8_t>& data, int sheet_id) {
466 std::ostringstream key;
467 key << "sheet_" << sheet_id << "_" << data.size();
468
469 // Add hash of data for uniqueness
470 size_t hash = 0;
471 for (size_t i = 0; i < std::min(data.size(), size_t(1024)); ++i) {
472 hash = hash * 31 + data[i];
473 }
474 key << "_" << hash;
475
476 return key.str();
477}
478
479void GraphicsOptimizer::UpdateOptimizationStats(const std::string& operation,
480 double value) {
481 optimization_stats_[operation] += value;
482}
483
484// GraphicsOptimizationScope implementation
485
487 OptimizationStrategy strategy, int sheet_count)
488 : strategy_(strategy),
489 sheet_count_(sheet_count),
490 timer_("graphics_optimize_scope") {
491 std::ostringstream op_name;
492 op_name << "graphics_optimize_" << static_cast<int>(strategy) << "_"
493 << sheet_count;
494 operation_name_ = op_name.str();
495}
496
498 // Timer automatically ends in destructor
499}
500
501void GraphicsOptimizationScope::AddSheet(int sheet_id, size_t original_size,
502 size_t optimized_size) {
503 result_.memory_saved += (original_size - optimized_size);
504}
505
507 result_ = result;
508}
509
510} // namespace gfx
511} // namespace yaze
SnesPalette OptimizePaletteForFormat(const SnesPalette &palette, BppFormat target_format, const std::vector< int > &used_colors)
Optimize palette for specific BPP format.
BppFormat DetectFormat(const std::vector< uint8_t > &data, int width, int height)
Detect BPP format from bitmap data.
std::vector< uint8_t > ConvertFormat(const std::vector< uint8_t > &data, BppFormat from_format, BppFormat to_format, int width, int height)
Convert bitmap data between BPP formats.
static BppFormatManager & Get()
const BppFormatInfo & GetFormatInfo(BppFormat format) const
Get BPP format information.
void SetResult(const OptimizationResult &result)
GraphicsOptimizationScope(OptimizationStrategy strategy, int sheet_count)
void AddSheet(int sheet_id, size_t original_size, size_t optimized_size)
Comprehensive graphics optimization system for YAZE ROM hacking.
std::unordered_map< std::string, SheetOptimizationData > optimization_cache_
void UpdateOptimizationStats(const std::string &operation, double value)
float CalculateColorEfficiency(const std::vector< uint8_t > &data, const SnesPalette &palette)
void Initialize()
Initialize the graphics optimizer.
BppFormat DetermineOptimalFormat(const std::vector< uint8_t > &data, const SnesPalette &palette, OptimizationStrategy strategy)
float CalculatePerformanceGain(BppFormat from_format, BppFormat to_format)
std::unordered_map< int, SheetOptimizationData > GetOptimizationRecommendations(const std::unordered_map< int, std::vector< uint8_t > > &sheets, const std::unordered_map< int, SnesPalette > &palettes)
Get optimization recommendations for all sheets.
std::string GenerateOptimizationReason(const SheetOptimizationData &data)
int CountUsedColors(const std::vector< uint8_t > &data, const SnesPalette &palette)
static GraphicsOptimizer & Get()
std::unordered_map< std::string, double > optimization_stats_
OptimizationResult ApplyOptimizations(const std::unordered_map< int, SheetOptimizationData > &recommendations, std::unordered_map< int, std::vector< uint8_t > > &sheets, std::unordered_map< int, SnesPalette > &palettes)
Apply optimization recommendations.
SheetOptimizationData AnalyzeSheet(const std::vector< uint8_t > &sheet_data, int sheet_id, const SnesPalette &palette)
Analyze graphics sheet for optimization opportunities.
OptimizationResult OptimizeSheets(const std::unordered_map< int, std::vector< uint8_t > > &sheets, const std::unordered_map< int, SnesPalette > &palettes, OptimizationStrategy strategy=OptimizationStrategy::kBalanced)
Optimize multiple graphics sheets.
std::unordered_map< std::string, double > GetOptimizationStats() const
Get optimization statistics.
size_t CalculateMemorySavings(BppFormat from_format, BppFormat to_format, const std::vector< uint8_t > &data)
void SetOptimizationParameters(float max_quality_loss=0.1f, size_t min_memory_savings=1024, float performance_threshold=0.05f)
Set optimization parameters.
bool ShouldOptimize(const SheetOptimizationData &data, OptimizationStrategy strategy)
OptimizationResult OptimizeSheet(const std::vector< uint8_t > &sheet_data, int sheet_id, const SnesPalette &palette, OptimizationStrategy strategy=OptimizationStrategy::kBalanced)
Optimize a single graphics sheet.
float CalculateQualityLoss(BppFormat from_format, BppFormat to_format, const std::vector< uint8_t > &data)
void ClearCache()
Clear optimization cache.
std::vector< int > AnalyzeColorDistribution(const std::vector< uint8_t > &data)
std::string GenerateCacheKey(const std::vector< uint8_t > &data, int sheet_id)
RAII timer for automatic timing management.
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
OptimizationStrategy
Graphics optimization strategy.
@ kBalanced
Balance memory, performance, and quality.
@ kPerformanceOptimized
Maximize rendering performance.
@ kMemoryOptimized
Minimize memory usage.
@ kQualityOptimized
Maintain highest quality.
BppFormat
BPP format enumeration for SNES graphics.
@ kBpp4
4 bits per pixel (16 colors)
@ kBpp3
3 bits per pixel (8 colors)
@ kBpp2
2 bits per pixel (4 colors)
@ kBpp8
8 bits per pixel (256 colors)
Graphics optimization result.
std::vector< BppFormat > recommended_formats
std::unordered_map< int, BppFormat > sheet_recommendations
Graphics sheet optimization data.