yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
arena.cc
Go to the documentation of this file.
2
4
5#include <algorithm>
6#include <chrono>
7
8#include "absl/strings/str_format.h"
10#include "util/log.h"
11#include "util/sdl_deleter.h"
13
14namespace yaze {
15namespace gfx {
16
18 renderer_ = renderer;
19 if (renderer != nullptr) {
20 is_shutdown_ = false;
21 }
22}
23
25 static Arena instance;
26 return instance;
27}
28
29Arena::Arena() : bg1_(512, 512), bg2_(512, 512) {
30 layer1_buffer_.fill(0);
31 layer2_buffer_.fill(0);
32}
33
35 // Use the safe shutdown method that handles cleanup properly
36 Shutdown();
37}
38
40 // Store generation at queue time for staleness detection
41 uint32_t gen = bitmap ? bitmap->generation() : 0;
42 texture_command_queue_.push_back({type, bitmap, gen});
43}
44
46 const Bitmap* bitmap) const {
47 if (bitmap == nullptr) {
48 return false;
49 }
50 const uint32_t generation = bitmap->generation();
51 return std::any_of(
53 [type, bitmap, generation](const TextureCommand& command) {
54 return command.type == type && command.bitmap == bitmap &&
55 command.generation == generation;
56 });
57}
58
62
64 // Texture commands retain Bitmap addresses. Remove them while the owner is
65 // still alive so no later generation check dereferences freed memory.
66 std::erase_if(texture_command_queue_,
67 [&bitmap](const TextureCommand& command) {
68 return command.bitmap == &bitmap;
69 });
70
71 const TextureHandle texture = bitmap.texture();
72 if (!is_shutdown_ && texture != nullptr &&
73 std::find(retired_texture_handles_.begin(),
75 texture) == retired_texture_handles_.end()) {
76 retired_texture_handles_.push_back(texture);
77 }
78
79 // Detach before returning the surface so a second retirement is a no-op and
80 // the soon-to-be-destroyed Bitmap no longer advertises live resources.
81 SDL_Surface* surface = bitmap.DetachSurfaceForArena();
82 (void)bitmap.DetachTextureForArena();
83 bitmap.MarkRetiredByArena();
84 // Shutdown clears the tracked surfaces before editor-owned Bitmap objects
85 // are destroyed. Do not return a stale pointer to the pool in that case.
86 if (surface != nullptr && surfaces_.contains(surface)) {
87 FreeSurface(surface);
88 }
89}
90
92 IRenderer* active_renderer = renderer ? renderer : renderer_;
93 if (active_renderer == nullptr) {
94 return 0;
95 }
96
97 size_t destroyed = 0;
98 auto it = retired_texture_handles_.begin();
99 while (it != retired_texture_handles_.end()) {
100 try {
101 active_renderer->DestroyTexture(*it);
102 it = retired_texture_handles_.erase(it);
103 ++destroyed;
104 } catch (...) {
105 LOG_ERROR("Arena", "Exception destroying retired bitmap texture");
106 ++it;
107 }
108 }
109 return destroyed;
110}
111
113 IRenderer* active_renderer = renderer ? renderer : renderer_;
114 if (!active_renderer || texture_command_queue_.empty()) {
115 return false;
116 }
117
118 auto it = texture_command_queue_.begin();
119 const auto& command = *it;
120 bool processed = false;
121 bool should_remove = true;
122
123 // Skip stale commands where bitmap was reallocated since queuing
124 if (command.bitmap && command.bitmap->generation() != command.generation) {
125 LOG_DEBUG("Arena", "Skipping stale texture command (gen %u != %u)",
126 command.generation, command.bitmap->generation());
127 texture_command_queue_.erase(it);
128 return false;
129 }
130
131 switch (command.type) {
133 if (command.bitmap && command.bitmap->surface() &&
134 command.bitmap->surface()->format && command.bitmap->is_active() &&
135 command.bitmap->width() > 0 && command.bitmap->height() > 0) {
136 const auto old_texture = command.bitmap->texture();
137 TextureHandle replacement = nullptr;
138 try {
139 replacement = active_renderer->CreateTexture(
140 command.bitmap->width(), command.bitmap->height());
141 } catch (...) {
142 LOG_ERROR("Arena", "Exception during single texture creation");
143 should_remove = false;
144 break;
145 }
146 if (!replacement) {
147 should_remove = false;
148 break;
149 }
150 try {
151 active_renderer->UpdateTexture(replacement, *command.bitmap);
152 } catch (...) {
153 LOG_ERROR("Arena", "Exception updating new single texture");
154 if (replacement != old_texture) {
155 try {
156 active_renderer->DestroyTexture(replacement);
157 } catch (...) {
158 LOG_ERROR("Arena", "Exception cleaning up failed single texture");
159 }
160 }
161 should_remove = false;
162 break;
163 }
164
165 command.bitmap->set_texture(replacement);
166 if (old_texture && old_texture != replacement) {
167 try {
168 active_renderer->DestroyTexture(old_texture);
169 } catch (...) {
170 LOG_ERROR("Arena", "Exception destroying replaced single texture");
171 }
172 }
173 processed = true;
174 }
175 break;
176 }
178 if (command.bitmap && command.bitmap->texture() &&
179 command.bitmap->surface() && command.bitmap->surface()->format &&
180 command.bitmap->is_active()) {
181 try {
182 active_renderer->UpdateTexture(command.bitmap->texture(),
183 *command.bitmap);
184 processed = true;
185 } catch (...) {
186 LOG_ERROR("Arena", "Exception during single texture update");
187 }
188 }
189 break;
190 }
192 if (command.bitmap && command.bitmap->texture()) {
193 try {
194 active_renderer->DestroyTexture(command.bitmap->texture());
195 command.bitmap->set_texture(nullptr);
196 processed = true;
197 } catch (...) {
198 LOG_ERROR("Arena", "Exception during single texture destruction");
199 }
200 }
201 break;
202 }
203 }
204
205 if (should_remove) {
206 texture_command_queue_.erase(it);
207 }
208 return processed;
209}
210
212 // Use provided renderer if available, otherwise use stored renderer
213 IRenderer* active_renderer = renderer ? renderer : renderer_;
214
215 if (!active_renderer) {
216 // Arena not initialized yet - defer processing
217 return;
218 }
219
220 if (texture_command_queue_.empty()) {
221 return;
222 }
223
224 // Performance optimization: Batch process textures with limits
225 // Process up to 8 texture operations per frame to avoid frame drops
226 constexpr size_t kMaxTexturesPerFrame = 8;
227 size_t processed = 0;
228
229 auto it = texture_command_queue_.begin();
230 while (it != texture_command_queue_.end() &&
231 processed < kMaxTexturesPerFrame) {
232 const auto& command = *it;
233 bool should_remove = true;
234
235 // Skip stale commands where bitmap was reallocated since queuing
236 if (command.bitmap && command.bitmap->generation() != command.generation) {
237 LOG_DEBUG("Arena", "Skipping stale texture command (gen %u != %u)",
238 command.generation, command.bitmap->generation());
239 it = texture_command_queue_.erase(it);
240 continue;
241 }
242
243 // CRITICAL: Replicate the exact short-circuit evaluation from working code
244 // We MUST check command.bitmap AND command.bitmap->surface() in one
245 // expression to avoid dereferencing invalid pointers
246
247 switch (command.type) {
249 // Create a new texture and update it with bitmap data
250 // Use short-circuit evaluation - if bitmap is invalid, never call
251 // ->surface()
252 if (command.bitmap && command.bitmap->surface() &&
253 command.bitmap->is_active() && command.bitmap->width() > 0 &&
254 command.bitmap->height() > 0) {
255
256 // DEBUG: Log texture creation with palette validation
257 auto* surf = command.bitmap->surface();
258 SDL_Palette* palette = platform::GetSurfacePalette(surf);
259 bool has_palette = palette != nullptr;
260 int color_count = has_palette ? palette->ncolors : 0;
261
262 // Log detailed surface state for debugging
264 "Arena::ProcessTextureQueue (CREATE)", surf);
266 "Arena::ProcessTextureQueue", has_palette, color_count);
267
268 // WARNING: Creating texture without proper palette will produce wrong
269 // colors
270 if (!has_palette) {
271 LOG_WARN("Arena",
272 "Creating texture from surface WITHOUT palette - "
273 "colors will be incorrect!");
275 "Arena::ProcessTextureQueue", 0, false,
276 "Surface has NO palette");
277 } else if (color_count < 90) {
278 LOG_WARN("Arena",
279 "Creating texture with only %d palette colors (expected "
280 "90 for dungeon)",
281 color_count);
283 "Arena::ProcessTextureQueue", 0, false,
284 absl::StrFormat("Low color count: %d", color_count));
285 }
286
288 "Arena::ProcessTextureQueue", 0, true,
289 "Calling CreateTexture...");
290 const auto old_texture = command.bitmap->texture();
291 TextureHandle replacement = nullptr;
292 try {
293 replacement = active_renderer->CreateTexture(
294 command.bitmap->width(), command.bitmap->height());
295 } catch (...) {
296 LOG_ERROR("Arena", "Exception during texture creation");
298 "Arena::ProcessTextureQueue", 0, false,
299 "EXCEPTION during texture creation");
300 should_remove = false;
301 break;
302 }
303
304 if (!replacement) {
306 "Arena::ProcessTextureQueue", 0, false,
307 "CreateTexture returned NULL");
308 should_remove = false;
309 break;
310 }
311
312 try {
313 active_renderer->UpdateTexture(replacement, *command.bitmap);
314 } catch (...) {
315 LOG_ERROR("Arena", "Exception updating new texture");
316 if (replacement != old_texture) {
317 try {
318 active_renderer->DestroyTexture(replacement);
319 } catch (...) {
320 LOG_ERROR("Arena", "Exception cleaning up failed texture");
321 }
322 }
323 should_remove = false;
324 break;
325 }
326
327 command.bitmap->set_texture(replacement);
328 if (old_texture && old_texture != replacement) {
329 try {
330 active_renderer->DestroyTexture(old_texture);
331 } catch (...) {
332 LOG_ERROR("Arena", "Exception destroying replaced texture");
333 }
334 }
336 "Arena::ProcessTextureQueue", 0, true, "CreateTexture SUCCESS");
337 processed++;
338 }
339 break;
340 }
342 // Update existing texture with current bitmap data
343 if (command.bitmap && command.bitmap->texture() &&
344 command.bitmap->surface() && command.bitmap->surface()->format &&
345 command.bitmap->is_active()) {
346 try {
347 active_renderer->UpdateTexture(command.bitmap->texture(),
348 *command.bitmap);
349 processed++;
350 } catch (...) {
351 LOG_ERROR("Arena", "Exception during texture update");
352 }
353 }
354 break;
355 }
357 if (command.bitmap && command.bitmap->texture()) {
358 try {
359 active_renderer->DestroyTexture(command.bitmap->texture());
360 command.bitmap->set_texture(nullptr);
361 processed++;
362 } catch (...) {
363 LOG_ERROR("Arena", "Exception during texture destruction");
364 }
365 }
366 break;
367 }
368 }
369
370 if (should_remove) {
371 it = texture_command_queue_.erase(it);
372 } else {
373 ++it;
374 }
375 }
376}
377
379 float budget_ms) {
380 using Clock = std::chrono::high_resolution_clock;
381 using Microseconds = std::chrono::microseconds;
382
383 IRenderer* active_renderer = renderer ? renderer : renderer_;
384 if (!active_renderer) {
385 return texture_command_queue_.empty();
386 }
387
388 if (texture_command_queue_.empty()) {
389 return true; // Queue is empty, all done
390 }
391
392 // Convert budget to microseconds for precise timing
393 const auto budget_us = static_cast<long long>(budget_ms * 1000.0f);
394 const auto start_time = Clock::now();
395
396 size_t textures_this_call = 0;
397
398 // Process textures until budget exhausted or queue empty
399 while (!texture_command_queue_.empty()) {
400 // Check time budget before each texture (not after, to avoid overshoot)
401 if (textures_this_call > 0) { // Always process at least one
402 auto elapsed =
403 std::chrono::duration_cast<Microseconds>(Clock::now() - start_time);
404 if (elapsed.count() >= budget_us) {
405 LOG_DEBUG("Arena",
406 "Budget exhausted: processed %zu textures in %.2fms, "
407 "%zu remaining",
408 textures_this_call, elapsed.count() / 1000.0f,
410 break;
411 }
412 }
413
414 // A failed CREATE remains queued for a later frame. Stop this budget pass
415 // when no command was removed so a retry cannot spin on the same front
416 // entry indefinitely.
417 const size_t queue_size_before = texture_command_queue_.size();
418 if (ProcessSingleTexture(active_renderer)) {
419 textures_this_call++;
420 } else if (texture_command_queue_.size() == queue_size_before) {
421 break;
422 }
423 }
424
425 // Update statistics
426 if (textures_this_call > 0) {
427 auto total_elapsed =
428 std::chrono::duration_cast<Microseconds>(Clock::now() - start_time);
429 float elapsed_ms = total_elapsed.count() / 1000.0f;
430
431 texture_queue_stats_.textures_processed += textures_this_call;
434
435 if (elapsed_ms > texture_queue_stats_.max_frame_time_ms) {
437 }
438
442 static_cast<float>(texture_queue_stats_.textures_processed);
443 }
444 }
445
446 return texture_command_queue_.empty();
447}
448
449SDL_Surface* Arena::AllocateSurface(int width, int height, int depth,
450 int format) {
451 // Try to get a surface from the pool first
452 for (auto it = surface_pool_.available_surfaces_.begin();
453 it != surface_pool_.available_surfaces_.end(); ++it) {
454 auto& info = surface_pool_.surface_info_[*it];
455 if (std::get<0>(info) == width && std::get<1>(info) == height &&
456 std::get<2>(info) == depth && std::get<3>(info) == format) {
457 SDL_Surface* surface = *it;
459 return surface;
460 }
461 }
462
463 // Create new surface if none available in pool
464 Uint32 sdl_format = GetSnesPixelFormat(format);
465 SDL_Surface* surface =
466 platform::CreateSurface(width, height, depth, sdl_format);
467
468 if (surface) {
469 auto surface_ptr =
470 std::unique_ptr<SDL_Surface, util::SDL_Surface_Deleter>(surface);
471 surfaces_[surface] = std::move(surface_ptr);
473 std::make_tuple(width, height, depth, format);
474 }
475
476 return surface;
477}
478
479void Arena::FreeSurface(SDL_Surface* surface) {
480 if (!surface)
481 return;
482
483 // Return surface to pool if space available
485 surface_pool_.available_surfaces_.push_back(surface);
486 } else {
487 // Remove from tracking maps
488 surface_pool_.surface_info_.erase(surface);
489 surfaces_.erase(surface);
490 }
491}
492
494 if (is_shutdown_) {
495 return;
496 }
497
498 // Process any remaining batch updates before shutdown
501
502 // Editor-owned Bitmap objects can outlive the renderer and window backend.
503 // Late retirement must only detach their stale resource fields.
504 is_shutdown_ = true;
505
506 // Clear LRU cache tracking (doesn't destroy textures, just tracking)
508
509 // Clear pool references first to prevent reuse during shutdown
514
515 // CRITICAL FIX: Clear containers in reverse order to prevent cleanup issues
516 // This ensures that dependent resources are freed before their dependencies
517 textures_.clear();
518 surfaces_.clear();
519
520 // Clear any remaining queue items
523 renderer_ = nullptr;
524}
525
526void Arena::NotifySheetModified(int sheet_index) {
527 if (sheet_index < 0 || sheet_index >= 223) {
528 LOG_WARN("Arena", "Invalid sheet index %d, ignoring notification",
529 sheet_index);
530 return;
531 }
532
533 auto& sheet = gfx_sheets_[sheet_index];
534 if (!sheet.is_active() || !sheet.surface()) {
535 LOG_DEBUG("Arena",
536 "Sheet %d not active or no surface, skipping notification",
537 sheet_index);
538 return;
539 }
540
541 // Queue texture update so changes are visible in all editors
542 if (sheet.texture()) {
544 LOG_DEBUG("Arena", "Queued texture update for modified sheet %d",
545 sheet_index);
546 } else {
547 // Create texture if it doesn't exist
549 LOG_DEBUG("Arena", "Queued texture creation for modified sheet %d",
550 sheet_index);
551 }
552}
553
554// ========== Palette Change Notification System ==========
555
556void Arena::NotifyPaletteModified(const std::string& group_name,
557 int palette_index) {
558 LOG_DEBUG("Arena", "Palette modified: group='%s', palette=%d",
559 group_name.c_str(), palette_index);
560
561 // Notify all registered listeners
562 for (const auto& [id, callback] : palette_listeners_) {
563 try {
564 callback(group_name, palette_index);
565 } catch (const std::exception& e) {
566 LOG_ERROR("Arena", "Exception in palette listener %d: %s", id, e.what());
567 }
568 }
569
570 LOG_DEBUG("Arena", "Notified %zu palette listeners",
571 palette_listeners_.size());
572}
573
575 int id = next_palette_listener_id_++;
576 palette_listeners_[id] = std::move(callback);
577 LOG_DEBUG("Arena", "Registered palette listener with ID %d", id);
578 return id;
579}
580
581void Arena::UnregisterPaletteListener(int listener_id) {
582 auto it = palette_listeners_.find(listener_id);
583 if (it != palette_listeners_.end()) {
584 palette_listeners_.erase(it);
585 LOG_DEBUG("Arena", "Unregistered palette listener with ID %d", listener_id);
586 }
587}
588
589// ========== LRU Sheet Texture Cache ==========
590
591void Arena::TouchSheet(int sheet_index) {
592 if (sheet_index < 0 || sheet_index >= 223) {
593 return;
594 }
595
596 auto map_it = sheet_lru_map_.find(sheet_index);
597 if (map_it != sheet_lru_map_.end()) {
598 // Sheet already in cache - move to front (most recently used)
599 sheet_lru_list_.erase(map_it->second);
600 sheet_lru_list_.push_front(sheet_index);
601 map_it->second = sheet_lru_list_.begin();
602 } else {
603 // New sheet - add to front
604 sheet_lru_list_.push_front(sheet_index);
605 sheet_lru_map_[sheet_index] = sheet_lru_list_.begin();
606 }
607}
608
610 if (sheet_index < 0 || sheet_index >= 223) {
611 return nullptr;
612 }
613
614 auto& sheet = gfx_sheets_[sheet_index];
615
616 // Check if sheet already has texture (cache hit)
617 bool had_texture = sheet.texture() != nullptr;
618
619 // Touch to update LRU order
620 TouchSheet(sheet_index);
621
622 if (had_texture) {
624 } else {
626
627 // Queue texture creation if sheet has valid surface
628 if (sheet.is_active() && sheet.surface()) {
630 }
631
632 // Check if we need to evict LRU sheets
634 EvictLRUSheets(0); // Evict until under max
635 }
636 }
637
639 return &sheet;
640}
641
642void Arena::SetSheetCacheSize(size_t max_size) {
643 // Clamp to valid range
644 sheet_cache_max_size_ = std::clamp(max_size, size_t{16}, size_t{223});
645
646 // Evict if current cache exceeds new size
649 }
650
651 LOG_INFO("Arena", "Sheet cache size set to %zu", sheet_cache_max_size_);
652}
653
654size_t Arena::EvictLRUSheets(size_t count) {
655 size_t evicted = 0;
656 size_t target_evictions = count;
657
658 // If count is 0, evict until we're under the max size
659 if (count == 0 && sheet_lru_map_.size() > sheet_cache_max_size_) {
660 target_evictions = sheet_lru_map_.size() - sheet_cache_max_size_;
661 }
662
663 // Evict from back of list (least recently used)
664 while (!sheet_lru_list_.empty() && evicted < target_evictions) {
665 int sheet_index = sheet_lru_list_.back();
666 auto& sheet = gfx_sheets_[sheet_index];
667
668 // Destroy texture if it exists
669 if (sheet.texture()) {
671 LOG_DEBUG("Arena", "Evicted LRU sheet %d texture", sheet_index);
672 evicted++;
674 }
675
676 // Remove from LRU tracking
677 sheet_lru_map_.erase(sheet_index);
678 sheet_lru_list_.pop_back();
679 }
680
682
683 if (evicted > 0) {
684 LOG_DEBUG("Arena", "Evicted %zu LRU sheet textures, %zu remaining", evicted,
685 sheet_lru_map_.size());
686 }
687
688 return evicted;
689}
690
692 sheet_lru_list_.clear();
693 sheet_lru_map_.clear();
695 LOG_DEBUG("Arena", "Cleared sheet cache tracking");
696}
697
698} // namespace gfx
699} // namespace yaze
Resource management arena for efficient graphics memory handling.
Definition arena.h:47
IRenderer * renderer_
Definition arena.h:393
size_t sheet_cache_max_size_
Definition arena.h:406
std::unordered_map< SDL_Surface *, std::unique_ptr< SDL_Surface, util::SDL_Surface_Deleter > > surfaces_
Definition arena.h:375
void Initialize(IRenderer *renderer)
Definition arena.cc:17
struct yaze::gfx::Arena::TexturePool texture_pool_
bool ProcessTextureQueueWithBudget(IRenderer *renderer, float budget_ms)
Process texture queue with a time budget.
Definition arena.cc:378
SDL_Surface * AllocateSurface(int width, int height, int depth, int format)
Definition arena.cc:449
std::unordered_map< TextureHandle, std::unique_ptr< SDL_Texture, util::SDL_Texture_Deleter > > textures_
Definition arena.h:371
void ClearTextureQueue()
Definition arena.cc:59
Bitmap * GetSheetWithCache(int sheet_index)
Get a sheet with automatic LRU tracking and texture creation.
Definition arena.cc:609
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
Definition arena.cc:39
int RegisterPaletteListener(PaletteChangeCallback callback)
Register a callback for palette change notifications.
Definition arena.cc:574
std::array< uint16_t, kTotalTiles > layer2_buffer_
Definition arena.h:365
bool HasPendingTextureCommand(TextureCommandType type, const Bitmap *bitmap) const
Definition arena.cc:45
void RetireBitmap(Bitmap &bitmap)
Explicitly retire resources owned by a bitmap that is being erased.
Definition arena.cc:63
size_t DrainRetiredBitmaps(IRenderer *renderer)
Destroy texture handles retired earlier in the frame.
Definition arena.cc:91
SheetCacheStats sheet_cache_stats_
Definition arena.h:407
std::function< void(const std::string &group_name, int palette_index)> PaletteChangeCallback
Definition arena.h:219
void FreeSurface(SDL_Surface *surface)
Definition arena.cc:479
void ProcessTextureQueue(IRenderer *renderer)
Definition arena.cc:211
std::array< gfx::Bitmap, 223 > gfx_sheets_
Definition arena.h:367
bool ProcessSingleTexture(IRenderer *renderer)
Process a single texture command for frame-budget-aware loading.
Definition arena.cc:112
std::unordered_map< int, std::list< int >::iterator > sheet_lru_map_
Definition arena.h:405
std::unordered_map< int, PaletteChangeCallback > palette_listeners_
Definition arena.h:398
std::vector< TextureHandle > retired_texture_handles_
Definition arena.h:392
std::vector< TextureCommand > texture_command_queue_
Definition arena.h:391
std::array< uint16_t, kTotalTiles > layer1_buffer_
Definition arena.h:364
bool is_shutdown_
Definition arena.h:394
void NotifySheetModified(int sheet_index)
Notify Arena that a graphics sheet has been modified.
Definition arena.cc:526
struct yaze::gfx::Arena::SurfacePool surface_pool_
void UnregisterPaletteListener(int listener_id)
Unregister a palette change listener.
Definition arena.cc:581
TextureQueueStats texture_queue_stats_
Definition arena.h:395
void Shutdown()
Definition arena.cc:493
void SetSheetCacheSize(size_t max_size)
Set the maximum number of sheet textures to keep cached.
Definition arena.cc:642
static Arena & Get()
Definition arena.cc:24
size_t EvictLRUSheets(size_t count=0)
Evict least recently used sheet textures.
Definition arena.cc:654
std::list< int > sheet_lru_list_
Definition arena.h:403
int next_palette_listener_id_
Definition arena.h:399
void NotifyPaletteModified(const std::string &group_name, int palette_index=-1)
Notify all listeners that a palette has been modified.
Definition arena.cc:556
void ClearSheetCache()
Clear all sheet texture cache tracking.
Definition arena.cc:691
void TouchSheet(int sheet_index)
Mark a graphics sheet as recently accessed.
Definition arena.cc:591
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:69
SDL_Surface * DetachSurfaceForArena() noexcept
Definition bitmap.h:420
TextureHandle texture() const
Definition bitmap.h:403
uint32_t generation() const
Definition bitmap.h:408
TextureHandle DetachTextureForArena() noexcept
Definition bitmap.h:425
void MarkRetiredByArena() noexcept
Definition bitmap.h:430
Defines an abstract interface for all rendering operations.
Definition irenderer.h:60
virtual TextureHandle CreateTexture(int width, int height)=0
Creates a new, empty texture.
virtual void UpdateTexture(TextureHandle texture, const Bitmap &bitmap)=0
Updates a texture with the pixel data from a Bitmap.
virtual void DestroyTexture(TextureHandle texture)=0
Destroys a texture and frees its associated resources.
void LogTextureCreation(const std::string &location, bool has_palette, int color_count)
static PaletteDebugger & Get()
void LogPaletteApplication(const std::string &location, int palette_id, bool success, const std::string &reason="")
void LogSurfaceState(const std::string &location, SDL_Surface *surface)
#define LOG_DEBUG(category, format,...)
Definition log.h:103
#define LOG_ERROR(category, format,...)
Definition log.h:110
#define LOG_WARN(category, format,...)
Definition log.h:108
#define LOG_INFO(category, format,...)
Definition log.h:106
void * TextureHandle
An abstract handle representing a texture.
Definition irenderer.h:47
Uint32 GetSnesPixelFormat(int format)
Convert bitmap format enum to SDL pixel format.
Definition bitmap.cc:33
SDL_Surface * CreateSurface(int width, int height, int depth, uint32_t format)
Create a surface using the appropriate API.
Definition sdl_compat.h:439
SDL_Palette * GetSurfacePalette(SDL_Surface *surface)
Get the palette attached to a surface.
Definition sdl_compat.h:392
SDL2/SDL3 compatibility layer.
static constexpr size_t MAX_POOL_SIZE
Definition arena.h:388
std::vector< SDL_Surface * > available_surfaces_
Definition arena.h:385
std::unordered_map< SDL_Surface *, std::tuple< int, int, int, int > > surface_info_
Definition arena.h:387
std::vector< TextureHandle > available_textures_
Definition arena.h:379
std::unordered_map< TextureHandle, std::pair< int, int > > texture_sizes_
Definition arena.h:380