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);
265 if (command.bitmap->IsIndexed()) {
267 "Arena::ProcessTextureQueue", has_palette, color_count);
268 }
269
270 // WARNING: Creating texture without proper palette will produce wrong
271 // colors
272 if (command.bitmap->IsIndexed() && !has_palette) {
273 LOG_WARN("Arena",
274 "Creating texture from surface WITHOUT palette - "
275 "colors will be incorrect!");
277 "Arena::ProcessTextureQueue", 0, false,
278 "Surface has NO palette");
279 }
280
282 "Arena::ProcessTextureQueue", 0, true,
283 "Calling CreateTexture...");
284 const auto old_texture = command.bitmap->texture();
285 TextureHandle replacement = nullptr;
286 try {
287 replacement = active_renderer->CreateTexture(
288 command.bitmap->width(), command.bitmap->height());
289 } catch (...) {
290 LOG_ERROR("Arena", "Exception during texture creation");
292 "Arena::ProcessTextureQueue", 0, false,
293 "EXCEPTION during texture creation");
294 should_remove = false;
295 break;
296 }
297
298 if (!replacement) {
300 "Arena::ProcessTextureQueue", 0, false,
301 "CreateTexture returned NULL");
302 should_remove = false;
303 break;
304 }
305
306 try {
307 active_renderer->UpdateTexture(replacement, *command.bitmap);
308 } catch (...) {
309 LOG_ERROR("Arena", "Exception updating new texture");
310 if (replacement != old_texture) {
311 try {
312 active_renderer->DestroyTexture(replacement);
313 } catch (...) {
314 LOG_ERROR("Arena", "Exception cleaning up failed texture");
315 }
316 }
317 should_remove = false;
318 break;
319 }
320
321 command.bitmap->set_texture(replacement);
322 if (old_texture && old_texture != replacement) {
323 try {
324 active_renderer->DestroyTexture(old_texture);
325 } catch (...) {
326 LOG_ERROR("Arena", "Exception destroying replaced texture");
327 }
328 }
330 "Arena::ProcessTextureQueue", 0, true, "CreateTexture SUCCESS");
331 processed++;
332 }
333 break;
334 }
336 // Update existing texture with current bitmap data
337 if (command.bitmap && command.bitmap->texture() &&
338 command.bitmap->surface() && command.bitmap->surface()->format &&
339 command.bitmap->is_active()) {
340 try {
341 active_renderer->UpdateTexture(command.bitmap->texture(),
342 *command.bitmap);
343 processed++;
344 } catch (...) {
345 LOG_ERROR("Arena", "Exception during texture update");
346 }
347 }
348 break;
349 }
351 if (command.bitmap && command.bitmap->texture()) {
352 try {
353 active_renderer->DestroyTexture(command.bitmap->texture());
354 command.bitmap->set_texture(nullptr);
355 processed++;
356 } catch (...) {
357 LOG_ERROR("Arena", "Exception during texture destruction");
358 }
359 }
360 break;
361 }
362 }
363
364 if (should_remove) {
365 it = texture_command_queue_.erase(it);
366 } else {
367 ++it;
368 }
369 }
370}
371
373 float budget_ms) {
374 using Clock = std::chrono::high_resolution_clock;
375 using Microseconds = std::chrono::microseconds;
376
377 IRenderer* active_renderer = renderer ? renderer : renderer_;
378 if (!active_renderer) {
379 return texture_command_queue_.empty();
380 }
381
382 if (texture_command_queue_.empty()) {
383 return true; // Queue is empty, all done
384 }
385
386 // Convert budget to microseconds for precise timing
387 const auto budget_us = static_cast<long long>(budget_ms * 1000.0f);
388 const auto start_time = Clock::now();
389
390 size_t textures_this_call = 0;
391
392 // Process textures until budget exhausted or queue empty
393 while (!texture_command_queue_.empty()) {
394 // Check time budget before each texture (not after, to avoid overshoot)
395 if (textures_this_call > 0) { // Always process at least one
396 auto elapsed =
397 std::chrono::duration_cast<Microseconds>(Clock::now() - start_time);
398 if (elapsed.count() >= budget_us) {
399 LOG_DEBUG("Arena",
400 "Budget exhausted: processed %zu textures in %.2fms, "
401 "%zu remaining",
402 textures_this_call, elapsed.count() / 1000.0f,
404 break;
405 }
406 }
407
408 // A failed CREATE remains queued for a later frame. Stop this budget pass
409 // when no command was removed so a retry cannot spin on the same front
410 // entry indefinitely.
411 const size_t queue_size_before = texture_command_queue_.size();
412 if (ProcessSingleTexture(active_renderer)) {
413 textures_this_call++;
414 } else if (texture_command_queue_.size() == queue_size_before) {
415 break;
416 }
417 }
418
419 // Update statistics
420 if (textures_this_call > 0) {
421 auto total_elapsed =
422 std::chrono::duration_cast<Microseconds>(Clock::now() - start_time);
423 float elapsed_ms = total_elapsed.count() / 1000.0f;
424
425 texture_queue_stats_.textures_processed += textures_this_call;
428
429 if (elapsed_ms > texture_queue_stats_.max_frame_time_ms) {
431 }
432
436 static_cast<float>(texture_queue_stats_.textures_processed);
437 }
438 }
439
440 return texture_command_queue_.empty();
441}
442
443SDL_Surface* Arena::AllocateSurface(int width, int height, int depth,
444 int format) {
445 // Try to get a surface from the pool first
446 for (auto it = surface_pool_.available_surfaces_.begin();
447 it != surface_pool_.available_surfaces_.end(); ++it) {
448 auto& info = surface_pool_.surface_info_[*it];
449 if (std::get<0>(info) == width && std::get<1>(info) == height &&
450 std::get<2>(info) == depth && std::get<3>(info) == format) {
451 SDL_Surface* surface = *it;
453 return surface;
454 }
455 }
456
457 // Create new surface if none available in pool
458 // Bitmap callers already resolved BitmapFormat to an SDL pixel format.
459 // Converting that numeric SDL value again silently falls back to INDEX8.
460 const Uint32 sdl_format = static_cast<Uint32>(format);
461 SDL_Surface* surface =
462 platform::CreateSurface(width, height, depth, sdl_format);
463
464 if (surface) {
465 auto surface_ptr =
466 std::unique_ptr<SDL_Surface, util::SDL_Surface_Deleter>(surface);
467 surfaces_[surface] = std::move(surface_ptr);
469 std::make_tuple(width, height, depth, format);
470 }
471
472 return surface;
473}
474
475void Arena::FreeSurface(SDL_Surface* surface) {
476 if (!surface)
477 return;
478
479 // Return surface to pool if space available
481 surface_pool_.available_surfaces_.push_back(surface);
482 } else {
483 // Remove from tracking maps
484 surface_pool_.surface_info_.erase(surface);
485 surfaces_.erase(surface);
486 }
487}
488
490 if (is_shutdown_) {
491 return;
492 }
493
494 // Process any remaining batch updates before shutdown
497
498 // Editor-owned Bitmap objects can outlive the renderer and window backend.
499 // Late retirement must only detach their stale resource fields.
500 is_shutdown_ = true;
501
502 // Clear LRU cache tracking (doesn't destroy textures, just tracking)
504
505 // Clear pool references first to prevent reuse during shutdown
510
511 // CRITICAL FIX: Clear containers in reverse order to prevent cleanup issues
512 // This ensures that dependent resources are freed before their dependencies
513 textures_.clear();
514 surfaces_.clear();
515
516 // Clear any remaining queue items
519 renderer_ = nullptr;
520}
521
522void Arena::BindGraphicsSheets(GraphicsSheets* sheets, const void* owner) {
523 auto* next = sheets ? sheets : &gfx_sheets_;
524 if (active_gfx_sheets_ != next) {
525 // LRU entries are sheet indices in the active storage, not global IDs.
527 active_gfx_sheets_ = next;
528 }
529 gfx_sheets_owner_ = sheets ? owner : nullptr;
530}
531
532void Arena::NotifySheetModified(int sheet_index) {
533 if (sheet_index < 0 || sheet_index >= 223) {
534 LOG_WARN("Arena", "Invalid sheet index %d, ignoring notification",
535 sheet_index);
536 return;
537 }
538
539 auto& sheet = gfx_sheets()[sheet_index];
540 if (!sheet.is_active() || !sheet.surface()) {
541 LOG_DEBUG("Arena",
542 "Sheet %d not active or no surface, skipping notification",
543 sheet_index);
544 return;
545 }
546
547 // Queue texture update so changes are visible in all editors
548 if (sheet.texture()) {
550 LOG_DEBUG("Arena", "Queued texture update for modified sheet %d",
551 sheet_index);
552 } else {
553 // Create texture if it doesn't exist
555 LOG_DEBUG("Arena", "Queued texture creation for modified sheet %d",
556 sheet_index);
557 }
558}
559
560// ========== Palette Change Notification System ==========
561
562void Arena::NotifyPaletteModified(const std::string& group_name,
563 int palette_index) {
564 LOG_DEBUG("Arena", "Palette modified: group='%s', palette=%d",
565 group_name.c_str(), palette_index);
566
567 // Notify all registered listeners
568 for (const auto& [id, callback] : palette_listeners_) {
569 try {
570 callback(group_name, palette_index);
571 } catch (const std::exception& e) {
572 LOG_ERROR("Arena", "Exception in palette listener %d: %s", id, e.what());
573 }
574 }
575
576 LOG_DEBUG("Arena", "Notified %zu palette listeners",
577 palette_listeners_.size());
578}
579
581 int id = next_palette_listener_id_++;
582 palette_listeners_[id] = std::move(callback);
583 LOG_DEBUG("Arena", "Registered palette listener with ID %d", id);
584 return id;
585}
586
587void Arena::UnregisterPaletteListener(int listener_id) {
588 auto it = palette_listeners_.find(listener_id);
589 if (it != palette_listeners_.end()) {
590 palette_listeners_.erase(it);
591 LOG_DEBUG("Arena", "Unregistered palette listener with ID %d", listener_id);
592 }
593}
594
595// ========== LRU Sheet Texture Cache ==========
596
597void Arena::TouchSheet(int sheet_index) {
598 if (sheet_index < 0 || sheet_index >= 223) {
599 return;
600 }
601
602 auto map_it = sheet_lru_map_.find(sheet_index);
603 if (map_it != sheet_lru_map_.end()) {
604 // Sheet already in cache - move to front (most recently used)
605 sheet_lru_list_.erase(map_it->second);
606 sheet_lru_list_.push_front(sheet_index);
607 map_it->second = sheet_lru_list_.begin();
608 } else {
609 // New sheet - add to front
610 sheet_lru_list_.push_front(sheet_index);
611 sheet_lru_map_[sheet_index] = sheet_lru_list_.begin();
612 }
613}
614
616 if (sheet_index < 0 || sheet_index >= 223) {
617 return nullptr;
618 }
619
620 auto& sheet = gfx_sheets()[sheet_index];
621
622 // Check if sheet already has texture (cache hit)
623 bool had_texture = sheet.texture() != nullptr;
624
625 // Touch to update LRU order
626 TouchSheet(sheet_index);
627
628 if (had_texture) {
630 } else {
632
633 // Queue texture creation if sheet has valid surface
634 if (sheet.is_active() && sheet.surface()) {
636 }
637
638 // Check if we need to evict LRU sheets
640 EvictLRUSheets(0); // Evict until under max
641 }
642 }
643
645 return &sheet;
646}
647
648void Arena::SetSheetCacheSize(size_t max_size) {
649 // Clamp to valid range
650 sheet_cache_max_size_ = std::clamp(max_size, size_t{16}, size_t{223});
651
652 // Evict if current cache exceeds new size
655 }
656
657 LOG_INFO("Arena", "Sheet cache size set to %zu", sheet_cache_max_size_);
658}
659
660size_t Arena::EvictLRUSheets(size_t count) {
661 size_t evicted = 0;
662 size_t target_evictions = count;
663
664 // If count is 0, evict until we're under the max size
665 if (count == 0 && sheet_lru_map_.size() > sheet_cache_max_size_) {
666 target_evictions = sheet_lru_map_.size() - sheet_cache_max_size_;
667 }
668
669 // Evict from back of list (least recently used)
670 while (!sheet_lru_list_.empty() && evicted < target_evictions) {
671 int sheet_index = sheet_lru_list_.back();
672 auto& sheet = gfx_sheets()[sheet_index];
673
674 // Destroy texture if it exists
675 if (sheet.texture()) {
677 LOG_DEBUG("Arena", "Evicted LRU sheet %d texture", sheet_index);
678 evicted++;
680 }
681
682 // Remove from LRU tracking
683 sheet_lru_map_.erase(sheet_index);
684 sheet_lru_list_.pop_back();
685 }
686
688
689 if (evicted > 0) {
690 LOG_DEBUG("Arena", "Evicted %zu LRU sheet textures, %zu remaining", evicted,
691 sheet_lru_map_.size());
692 }
693
694 return evicted;
695}
696
698 sheet_lru_list_.clear();
699 sheet_lru_map_.clear();
701 LOG_DEBUG("Arena", "Cleared sheet cache tracking");
702}
703
704} // namespace gfx
705} // namespace yaze
Resource management arena for efficient graphics memory handling.
Definition arena.h:47
IRenderer * renderer_
Definition arena.h:406
size_t sheet_cache_max_size_
Definition arena.h:419
const void * gfx_sheets_owner_
Definition arena.h:380
std::unordered_map< SDL_Surface *, std::unique_ptr< SDL_Surface, util::SDL_Surface_Deleter > > surfaces_
Definition arena.h:388
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:372
SDL_Surface * AllocateSurface(int width, int height, int depth, int format)
Definition arena.cc:443
GraphicsSheets gfx_sheets_
Definition arena.h:378
std::unordered_map< TextureHandle, std::unique_ptr< SDL_Texture, util::SDL_Texture_Deleter > > textures_
Definition arena.h:384
std::array< gfx::Bitmap, 223 > GraphicsSheets
Get reference to all graphics sheets.
Definition arena.h:177
void ClearTextureQueue()
Definition arena.cc:59
Bitmap * GetSheetWithCache(int sheet_index)
Get a sheet with automatic LRU tracking and texture creation.
Definition arena.cc:615
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
Definition arena.cc:39
int RegisterPaletteListener(PaletteChangeCallback callback)
Register a callback for palette change notifications.
Definition arena.cc:580
GraphicsSheets * active_gfx_sheets_
Definition arena.h:379
std::array< uint16_t, kTotalTiles > layer2_buffer_
Definition arena.h:376
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
GraphicsSheets & gfx_sheets()
Definition arena.h:178
SheetCacheStats sheet_cache_stats_
Definition arena.h:420
std::function< void(const std::string &group_name, int palette_index)> PaletteChangeCallback
Definition arena.h:230
void FreeSurface(SDL_Surface *surface)
Definition arena.cc:475
void ProcessTextureQueue(IRenderer *renderer)
Definition arena.cc:211
void BindGraphicsSheets(GraphicsSheets *sheets, const void *owner)
Definition arena.cc:522
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:418
std::unordered_map< int, PaletteChangeCallback > palette_listeners_
Definition arena.h:411
std::vector< TextureHandle > retired_texture_handles_
Definition arena.h:405
std::vector< TextureCommand > texture_command_queue_
Definition arena.h:404
std::array< uint16_t, kTotalTiles > layer1_buffer_
Definition arena.h:375
bool is_shutdown_
Definition arena.h:407
void NotifySheetModified(int sheet_index)
Notify Arena that a graphics sheet has been modified.
Definition arena.cc:532
struct yaze::gfx::Arena::SurfacePool surface_pool_
void UnregisterPaletteListener(int listener_id)
Unregister a palette change listener.
Definition arena.cc:587
TextureQueueStats texture_queue_stats_
Definition arena.h:408
void Shutdown()
Definition arena.cc:489
void SetSheetCacheSize(size_t max_size)
Set the maximum number of sheet textures to keep cached.
Definition arena.cc:648
static Arena & Get()
Definition arena.cc:24
size_t EvictLRUSheets(size_t count=0)
Evict least recently used sheet textures.
Definition arena.cc:660
std::list< int > sheet_lru_list_
Definition arena.h:416
int next_palette_listener_id_
Definition arena.h:412
void NotifyPaletteModified(const std::string &group_name, int palette_index=-1)
Notify all listeners that a palette has been modified.
Definition arena.cc:562
void ClearSheetCache()
Clear all sheet texture cache tracking.
Definition arena.cc:697
void TouchSheet(int sheet_index)
Mark a graphics sheet as recently accessed.
Definition arena.cc:597
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:70
SDL_Surface * DetachSurfaceForArena() noexcept
Definition bitmap.h:428
TextureHandle texture() const
Definition bitmap.h:410
uint32_t generation() const
Definition bitmap.h:415
TextureHandle DetachTextureForArena() noexcept
Definition bitmap.h:433
void MarkRetiredByArena() noexcept
Definition bitmap.h:438
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:133
#define LOG_ERROR(category, format,...)
Definition log.h:140
#define LOG_WARN(category, format,...)
Definition log.h:138
#define LOG_INFO(category, format,...)
Definition log.h:136
void * TextureHandle
An abstract handle representing a texture.
Definition irenderer.h:47
SDL_Surface * CreateSurface(int width, int height, int depth, uint32_t format)
Create a surface using the appropriate API.
Definition sdl_compat.h:445
SDL_Palette * GetSurfacePalette(SDL_Surface *surface)
Get the palette attached to a surface.
Definition sdl_compat.h:398
SDL2/SDL3 compatibility layer.
static constexpr size_t MAX_POOL_SIZE
Definition arena.h:401
std::vector< SDL_Surface * > available_surfaces_
Definition arena.h:398
std::unordered_map< SDL_Surface *, std::tuple< int, int, int, int > > surface_info_
Definition arena.h:400
std::vector< TextureHandle > available_textures_
Definition arena.h:392
std::unordered_map< TextureHandle, std::pair< int, int > > texture_sizes_
Definition arena.h:393