7#include "absl/strings/str_format.h"
21 static Arena instance;
37 uint32_t gen = bitmap ? bitmap->
generation() : 0;
52 const auto& command = *it;
53 bool processed =
false;
56 if (command.bitmap && command.bitmap->generation() != command.generation) {
57 LOG_DEBUG(
"Arena",
"Skipping stale texture command (gen %u != %u)",
58 command.generation, command.bitmap->generation());
63 switch (command.type) {
65 if (command.bitmap && command.bitmap->surface() &&
66 command.bitmap->surface()->format && command.bitmap->is_active() &&
67 command.bitmap->width() > 0 && command.bitmap->height() > 0) {
70 command.bitmap->width(), command.bitmap->height());
72 command.bitmap->set_texture(texture);
77 LOG_ERROR(
"Arena",
"Exception during single texture creation");
83 if (command.bitmap && command.bitmap->texture() &&
84 command.bitmap->surface() && command.bitmap->surface()->format &&
85 command.bitmap->is_active()) {
91 LOG_ERROR(
"Arena",
"Exception during single texture update");
97 if (command.bitmap && command.bitmap->texture()) {
100 command.bitmap->set_texture(
nullptr);
103 LOG_ERROR(
"Arena",
"Exception during single texture destruction");
119 if (!active_renderer) {
130 constexpr size_t kMaxTexturesPerFrame = 8;
131 size_t processed = 0;
135 processed < kMaxTexturesPerFrame) {
136 const auto& command = *it;
137 bool should_remove =
true;
140 if (command.bitmap && command.bitmap->generation() != command.generation) {
141 LOG_DEBUG(
"Arena",
"Skipping stale texture command (gen %u != %u)",
142 command.generation, command.bitmap->generation());
151 switch (command.type) {
156 if (command.bitmap && command.bitmap->surface() &&
157 command.bitmap->is_active() && command.bitmap->width() > 0 &&
158 command.bitmap->height() > 0) {
161 auto* surf = command.bitmap->surface();
163 bool has_palette = palette !=
nullptr;
164 int color_count = has_palette ? palette->ncolors : 0;
168 "Arena::ProcessTextureQueue (CREATE)", surf);
170 "Arena::ProcessTextureQueue", has_palette, color_count);
176 "Creating texture from surface WITHOUT palette - "
177 "colors will be incorrect!");
179 "Arena::ProcessTextureQueue", 0,
false,
180 "Surface has NO palette");
181 }
else if (color_count < 90) {
183 "Creating texture with only %d palette colors (expected "
187 "Arena::ProcessTextureQueue", 0,
false,
188 absl::StrFormat(
"Low color count: %d", color_count));
193 "Arena::ProcessTextureQueue", 0,
true,
194 "Calling CreateTexture...");
197 command.bitmap->width(), command.bitmap->height());
201 "Arena::ProcessTextureQueue", 0,
true,
202 "CreateTexture SUCCESS");
204 command.bitmap->set_texture(texture);
209 "Arena::ProcessTextureQueue", 0,
false,
210 "CreateTexture returned NULL");
211 should_remove =
false;
214 LOG_ERROR(
"Arena",
"Exception during texture creation");
216 "Arena::ProcessTextureQueue", 0,
false,
217 "EXCEPTION during texture creation");
218 should_remove =
true;
225 if (command.bitmap && command.bitmap->texture() &&
226 command.bitmap->surface() && command.bitmap->surface()->format &&
227 command.bitmap->is_active()) {
233 LOG_ERROR(
"Arena",
"Exception during texture update");
239 if (command.bitmap && command.bitmap->texture()) {
242 command.bitmap->set_texture(
nullptr);
245 LOG_ERROR(
"Arena",
"Exception during texture destruction");
266 if (std::get<0>(info) == width && std::get<1>(info) == height &&
267 std::get<2>(info) == depth && std::get<3>(info) == format) {
268 SDL_Surface* surface = *it;
276 SDL_Surface* surface =
281 std::unique_ptr<SDL_Surface, util::SDL_Surface_Deleter>(surface);
282 surfaces_[surface] = std::move(surface_ptr);
284 std::make_tuple(width, height, depth, format);
324 if (sheet_index < 0 || sheet_index >= 223) {
325 LOG_WARN(
"Arena",
"Invalid sheet index %d, ignoring notification",
331 if (!sheet.is_active() || !sheet.surface()) {
333 "Sheet %d not active or no surface, skipping notification",
339 if (sheet.texture()) {
341 LOG_DEBUG(
"Arena",
"Queued texture update for modified sheet %d",
346 LOG_DEBUG(
"Arena",
"Queued texture creation for modified sheet %d",
355 LOG_DEBUG(
"Arena",
"Palette modified: group='%s', palette=%d",
356 group_name.c_str(), palette_index);
361 callback(group_name, palette_index);
362 }
catch (
const std::exception& e) {
363 LOG_ERROR(
"Arena",
"Exception in palette listener %d: %s",
id, e.what());
367 LOG_DEBUG(
"Arena",
"Notified %zu palette listeners",
374 LOG_DEBUG(
"Arena",
"Registered palette listener with ID %d",
id);
382 LOG_DEBUG(
"Arena",
"Unregistered palette listener with ID %d", listener_id);
Resource management arena for efficient graphics memory handling.
std::unordered_map< SDL_Surface *, std::unique_ptr< SDL_Surface, util::SDL_Surface_Deleter > > surfaces_
void Initialize(IRenderer *renderer)
struct yaze::gfx::Arena::TexturePool texture_pool_
SDL_Surface * AllocateSurface(int width, int height, int depth, int format)
std::unordered_map< TextureHandle, std::unique_ptr< SDL_Texture, util::SDL_Texture_Deleter > > textures_
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
int RegisterPaletteListener(PaletteChangeCallback callback)
Register a callback for palette change notifications.
std::array< uint16_t, kTotalTiles > layer2_buffer_
std::function< void(const std::string &group_name, int palette_index)> PaletteChangeCallback
void FreeSurface(SDL_Surface *surface)
void ProcessTextureQueue(IRenderer *renderer)
std::array< gfx::Bitmap, 223 > gfx_sheets_
bool ProcessSingleTexture(IRenderer *renderer)
Process a single texture command for frame-budget-aware loading.
std::unordered_map< int, PaletteChangeCallback > palette_listeners_
std::vector< TextureCommand > texture_command_queue_
std::array< uint16_t, kTotalTiles > layer1_buffer_
void NotifySheetModified(int sheet_index)
Notify Arena that a graphics sheet has been modified.
struct yaze::gfx::Arena::SurfacePool surface_pool_
void UnregisterPaletteListener(int listener_id)
Unregister a palette change listener.
int next_palette_listener_id_
void NotifyPaletteModified(const std::string &group_name, int palette_index=-1)
Notify all listeners that a palette has been modified.
Represents a bitmap image optimized for SNES ROM hacking.
uint32_t generation() const
Defines an abstract interface for all rendering operations.
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,...)
#define LOG_ERROR(category, format,...)
#define LOG_WARN(category, format,...)
Uint32 GetSnesPixelFormat(int format)
Convert bitmap format enum to SDL pixel format.
SDL2/SDL3 compatibility layer.
static constexpr size_t MAX_POOL_SIZE
std::vector< SDL_Surface * > available_surfaces_
std::unordered_map< SDL_Surface *, std::tuple< int, int, int, int > > surface_info_
std::vector< TextureHandle > available_textures_
std::unordered_map< TextureHandle, std::pair< int, int > > texture_sizes_