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
205 std::unordered_map<int, SheetOptimizationData> recommendations;
206
207 for (const auto& [sheet_id, sheet_data] : sheets) {
208 auto palette_it = palettes.find(sheet_id);
209 if (palette_it == palettes.end()) {
210 continue;
211 }
212
213 recommendations[sheet_id] =
214 AnalyzeSheet(sheet_data, sheet_id, palette_it->second);
215 }
216
217 return recommendations;
218}
219
221 const std::unordered_map<int, SheetOptimizationData>& recommendations,
222 std::unordered_map<int, std::vector<uint8_t>>& sheets,
223 std::unordered_map<int, SnesPalette>& palettes) {
224
225 ScopedTimer timer("graphics_apply_optimizations");
226
227 OptimizationResult result;
228 result.success = true;
229
230 size_t total_memory_saved = 0;
231 int optimized_sheets = 0;
232
233 for (const auto& [sheet_id, data] : recommendations) {
234 if (!data.is_convertible) {
235 continue;
236 }
237
238 auto sheet_it = sheets.find(sheet_id);
239 if (sheet_it == sheets.end()) {
240 continue;
241 }
242
243 try {
244 // Convert the sheet data
245 auto converted_data = BppFormatManager::Get().ConvertFormat(
246 sheet_it->second, data.current_format, data.recommended_format, 128,
247 32);
248
249 // Update the sheet
250 sheet_it->second = converted_data;
251
252 // Optimize palette if needed
253 auto palette_it = palettes.find(sheet_id);
254 if (palette_it != palettes.end()) {
255 std::vector<int> used_colors;
256 for (int i = 0; i < data.colors_used; ++i) {
257 used_colors.push_back(i);
258 }
259
260 palette_it->second = BppFormatManager::Get().OptimizePaletteForFormat(
261 palette_it->second, data.recommended_format, used_colors);
262 }
263
264 total_memory_saved += data.current_size - data.optimized_size;
265 optimized_sheets++;
266
267 result.sheet_recommendations[sheet_id] = data.recommended_format;
268
269 } catch (const std::exception& e) {
270 SDL_Log("Failed to optimize sheet %d: %s", sheet_id, e.what());
271 }
272 }
273
274 result.memory_saved = total_memory_saved;
275 result.message = "Optimized " + std::to_string(optimized_sheets) + " sheets";
276
277 UpdateOptimizationStats("optimizations_applied",
278 static_cast<double>(optimized_sheets));
279 UpdateOptimizationStats("total_memory_saved",
280 static_cast<double>(total_memory_saved));
281
282 return result;
283}
284
285std::unordered_map<std::string, double>
289
294
296 size_t min_memory_savings,
297 float performance_threshold) {
298 max_quality_loss_ = max_quality_loss;
299 min_memory_savings_ = min_memory_savings;
300 performance_threshold_ = performance_threshold;
301}
302
303// Helper method implementations
304
306 const std::vector<uint8_t>& data, const SnesPalette& palette,
307 OptimizationStrategy strategy) {
308 int colors_used = CountUsedColors(data, palette);
309
310 // Determine optimal format based on color usage and strategy
311 switch (strategy) {
313 if (colors_used <= 4)
314 return BppFormat::kBpp2;
315 if (colors_used <= 8)
316 return BppFormat::kBpp3;
317 if (colors_used <= 16)
318 return BppFormat::kBpp4;
319 break;
320
322 // Prefer formats that work well with atlas rendering
323 if (colors_used <= 16)
324 return BppFormat::kBpp4;
325 break;
326
328 // Only optimize if significant memory savings
329 if (colors_used <= 4)
330 return BppFormat::kBpp2;
331 break;
332
334 if (colors_used <= 4)
335 return BppFormat::kBpp2;
336 if (colors_used <= 8)
337 return BppFormat::kBpp3;
338 if (colors_used <= 16)
339 return BppFormat::kBpp4;
340 break;
341 }
342
343 return BppFormat::kBpp8; // Default to 8BPP
344}
345
347 BppFormat from_format, BppFormat to_format,
348 const std::vector<uint8_t>& data) {
349 if (from_format == to_format)
350 return 0.0f;
351
352 // Higher BPP to lower BPP conversions may lose quality
353 if (static_cast<int>(from_format) > static_cast<int>(to_format)) {
354 int bpp_diff = static_cast<int>(from_format) - static_cast<int>(to_format);
355 return std::min(
356 1.0f, static_cast<float>(bpp_diff) * 0.1f); // 10% loss per BPP level
357 }
358
359 return 0.0f; // Lower to higher BPP is lossless
360}
361
363 BppFormat from_format, BppFormat to_format,
364 const std::vector<uint8_t>& data) {
365 if (from_format == to_format)
366 return 0;
367
368 const auto& from_info = BppFormatManager::Get().GetFormatInfo(from_format);
369 const auto& to_info = BppFormatManager::Get().GetFormatInfo(to_format);
370
371 size_t from_size = data.size();
372 size_t to_size =
373 (from_size * to_info.bits_per_pixel) / from_info.bits_per_pixel;
374
375 return from_size - to_size;
376}
377
379 BppFormat to_format) {
380 if (from_format == to_format)
381 return 0.0f;
382
383 // Lower BPP formats generally render faster
384 if (static_cast<int>(from_format) > static_cast<int>(to_format)) {
385 int bpp_diff = static_cast<int>(from_format) - static_cast<int>(to_format);
386 return std::min(
387 0.5f, static_cast<float>(bpp_diff) * 0.1f); // 10% gain per BPP level
388 }
389
390 return 0.0f;
391}
392
394 OptimizationStrategy strategy) {
395 if (!data.is_convertible)
396 return false;
397
398 switch (strategy) {
400 return data.compression_ratio > 1.2f; // At least 20% savings
401
403 return data.compression_ratio > 1.1f; // At least 10% savings
404
406 return data.compression_ratio > 1.5f; // At least 50% savings
407
409 return data.compression_ratio > 1.15f; // At least 15% savings
410 }
411
412 return false;
413}
414
416 const SheetOptimizationData& data) {
417 std::ostringstream reason;
418
419 reason << "Convert from "
421 << " to "
423 << " (uses " << data.colors_used << " colors, " << std::fixed
424 << std::setprecision(1) << (data.compression_ratio - 1.0f) * 100.0f
425 << "% memory savings)";
426
427 return reason.str();
428}
429
430int GraphicsOptimizer::CountUsedColors(const std::vector<uint8_t>& data,
431 const SnesPalette& palette) {
432 std::vector<bool> used_colors(palette.size(), false);
433
434 for (uint8_t pixel : data) {
435 if (pixel < palette.size()) {
436 used_colors[pixel] = true;
437 }
438 }
439
440 int count = 0;
441 for (bool used : used_colors) {
442 if (used)
443 count++;
444 }
445
446 return count;
447}
448
450 const std::vector<uint8_t>& data, const SnesPalette& palette) {
451 int used_colors = CountUsedColors(data, palette);
452 return static_cast<float>(used_colors) / palette.size();
453}
454
456 const std::vector<uint8_t>& data) {
457 std::vector<int> distribution(256, 0);
458
459 for (uint8_t pixel : data) {
460 distribution[pixel]++;
461 }
462
463 return distribution;
464}
465
467 const std::vector<uint8_t>& data, int sheet_id) {
468 std::ostringstream key;
469 key << "sheet_" << sheet_id << "_" << data.size();
470
471 // Add hash of data for uniqueness
472 size_t hash = 0;
473 for (size_t i = 0; i < std::min(data.size(), size_t(1024)); ++i) {
474 hash = hash * 31 + data[i];
475 }
476 key << "_" << hash;
477
478 return key.str();
479}
480
481void GraphicsOptimizer::UpdateOptimizationStats(const std::string& operation,
482 double value) {
483 optimization_stats_[operation] += value;
484}
485
486// GraphicsOptimizationScope implementation
487
489 OptimizationStrategy strategy, int sheet_count)
490 : strategy_(strategy),
491 sheet_count_(sheet_count),
492 timer_("graphics_optimize_scope") {
493 std::ostringstream op_name;
494 op_name << "graphics_optimize_" << static_cast<int>(strategy) << "_"
495 << sheet_count;
496 operation_name_ = op_name.str();
497}
498
500 // Timer automatically ends in destructor
501}
502
503void GraphicsOptimizationScope::AddSheet(int sheet_id, size_t original_size,
504 size_t optimized_size) {
505 result_.memory_saved += (original_size - optimized_size);
506}
507
509 result_ = result;
510}
511
512} // namespace gfx
513} // 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)
Main namespace for the application.
Graphics optimization result.
std::vector< BppFormat > recommended_formats
std::unordered_map< int, BppFormat > sheet_recommendations
Graphics sheet optimization data.