diff --git a/source/defines.hpp b/source/defines.hpp index eb77580..e5b0e53 100644 --- a/source/defines.hpp +++ b/source/defines.hpp @@ -59,10 +59,22 @@ constexpr float BALL_SPAWN_MARGIN = 0.15f; // Margen lateral para spawn (0.25 = // Escenarios de número de pelotas (teclas 1-8) // Fase 1 (instanced rendering): límit pràctic ~100K a 60fps (physics bound) -constexpr int BALL_COUNT_SCENARIOS[8] = {10, 50, 100, 500, 1000, 5000, 10000, 100000}; +constexpr int SCENE_BALLS_1 = 10; +constexpr int SCENE_BALLS_2 = 50; +constexpr int SCENE_BALLS_3 = 100; +constexpr int SCENE_BALLS_4 = 500; +constexpr int SCENE_BALLS_5 = 1000; +constexpr int SCENE_BALLS_6 = 5000; +constexpr int SCENE_BALLS_7 = 10000; +constexpr int SCENE_BALLS_8 = 50000; // Máximo escenario estándar (tecla 8) + +constexpr int SCENARIO_COUNT = 8; +constexpr int BALL_COUNT_SCENARIOS[SCENARIO_COUNT] = { + SCENE_BALLS_1, SCENE_BALLS_2, SCENE_BALLS_3, SCENE_BALLS_4, + SCENE_BALLS_5, SCENE_BALLS_6, SCENE_BALLS_7, SCENE_BALLS_8 +}; // Límites de escenario para modos automáticos (índices en BALL_COUNT_SCENARIOS) -// BALL_COUNT_SCENARIOS = {10, 50, 100, 500, 1000, 5000, 10000, 50000} // 0 1 2 3 4 5 6 7 constexpr int DEMO_AUTO_MIN_SCENARIO = 2; // mínimo 100 bolas constexpr int DEMO_AUTO_MAX_SCENARIO = 7; // máximo sin restricción hardware (ajustado por benchmark) diff --git a/source/engine.cpp b/source/engine.cpp index 7b3e219..663a533 100644 --- a/source/engine.cpp +++ b/source/engine.cpp @@ -282,6 +282,12 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen, AppMod // Inicializar ThemeManager PRIMERO (requerido por Notifier y SceneManager) theme_manager_ = std::make_unique(); theme_manager_->initialize(); + { + int max_balls = BALL_COUNT_SCENARIOS[DEMO_AUTO_MAX_SCENARIO]; + if (custom_scenario_enabled_ && custom_scenario_balls_ > max_balls) + max_balls = custom_scenario_balls_; + theme_manager_->setMaxBallCount(max_balls); + } // Inicializar SceneManager (gestión de bolas y física) scene_manager_ = std::make_unique(current_screen_width_, current_screen_height_); diff --git a/source/gpu/gpu_sprite_batch.cpp b/source/gpu/gpu_sprite_batch.cpp index e9c9d1c..17ebe2a 100644 --- a/source/gpu/gpu_sprite_batch.cpp +++ b/source/gpu/gpu_sprite_batch.cpp @@ -9,10 +9,10 @@ bool GpuSpriteBatch::init(SDL_GPUDevice* device, int max_sprites) { max_sprites_ = max_sprites; - // Pre-allocate GPU buffers large enough for (max_sprites_ + 1) quads. - // The +1 reserves a guaranteed slot for the fullscreen overlay. - Uint32 max_verts = static_cast(max_sprites_ + 1) * 4; - Uint32 max_indices = static_cast(max_sprites_ + 1) * 6; + // Pre-allocate GPU buffers large enough for (max_sprites_ + 2) quads. + // The +2 reserves one slot for the background quad and one for the fullscreen overlay. + Uint32 max_verts = static_cast(max_sprites_ + 2) * 4; + Uint32 max_indices = static_cast(max_sprites_ + 2) * 6; Uint32 vb_size = max_verts * sizeof(GpuVertex); Uint32 ib_size = max_indices * sizeof(uint32_t); @@ -55,8 +55,8 @@ bool GpuSpriteBatch::init(SDL_GPUDevice* device, int max_sprites) { return false; } - vertices_.reserve(static_cast(max_sprites_ + 1) * 4); - indices_.reserve(static_cast(max_sprites_ + 1) * 6); + vertices_.reserve(static_cast(max_sprites_ + 2) * 4); + indices_.reserve(static_cast(max_sprites_ + 2) * 6); return true; } @@ -130,10 +130,17 @@ void GpuSpriteBatch::addSprite(float x, float y, float w, float h, } void GpuSpriteBatch::addFullscreenOverlay() { + // El overlay es un slot reservado fuera del espacio de max_sprites_, igual que el background. + // Escribe directamente sin pasar por el guard de pushQuad(). overlay_index_offset_ = static_cast(indices_.size()); - size_t before = indices_.size(); - pushQuad(-1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f); - overlay_index_count_ = static_cast(indices_.size() - before); // 0 si pushQuad falló, 6 si ok + uint32_t vi = static_cast(vertices_.size()); + vertices_.push_back({ -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f }); + vertices_.push_back({ 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f }); + vertices_.push_back({ 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }); + vertices_.push_back({ -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }); + indices_.push_back(vi + 0); indices_.push_back(vi + 1); indices_.push_back(vi + 2); + indices_.push_back(vi + 2); indices_.push_back(vi + 3); indices_.push_back(vi + 0); + overlay_index_count_ = 6; } bool GpuSpriteBatch::uploadBatch(SDL_GPUDevice* device, SDL_GPUCommandBuffer* cmd_buf) { @@ -182,7 +189,8 @@ void GpuSpriteBatch::toNDC(float px, float py, void GpuSpriteBatch::pushQuad(float ndx0, float ndy0, float ndx1, float ndy1, float u0, float v0, float u1, float v1, float r, float g, float b, float a) { - if (vertices_.size() + 4 > static_cast(max_sprites_) * 4) return; + // +1 reserva el slot del background que ya entró sin pasar por este guard. + if (vertices_.size() + 4 > static_cast(max_sprites_ + 1) * 4) return; uint32_t vi = static_cast(vertices_.size()); // TL, TR, BR, BL diff --git a/source/theme_manager.cpp b/source/theme_manager.cpp index 6b9eb0c..0010547 100644 --- a/source/theme_manager.cpp +++ b/source/theme_manager.cpp @@ -640,10 +640,10 @@ std::unique_ptr ThemeManager::captureCurrentSnapshot() const { snapshot->name_en = themes_[current_theme_index_]->getNameEN(); snapshot->name_es = themes_[current_theme_index_]->getNameES(); - // Capturar colores de pelotas (suficientes para escenario máximo: 50,000) - // Esto asegura LERP correcto incluso en escenarios grandes - snapshot->ball_colors.reserve(50000); - for (size_t i = 0; i < 50000; i++) { + // Capturar colores de pelotas para el máximo real de esta sesión + // (SCENE_BALLS_8 o más si hay escenario custom) + snapshot->ball_colors.reserve(max_ball_count_); + for (int i = 0; i < max_ball_count_; i++) { snapshot->ball_colors.push_back( themes_[current_theme_index_]->getBallColor(i, 0.0f) ); diff --git a/source/theme_manager.hpp b/source/theme_manager.hpp index fafe4b8..f942b4c 100644 --- a/source/theme_manager.hpp +++ b/source/theme_manager.hpp @@ -43,6 +43,7 @@ class ThemeManager { // Inicialización void initialize(); // Inicializa 15 temas unificados (9 estáticos + 6 dinámicos) + void setMaxBallCount(int n) { max_ball_count_ = n; } // Máximo real (escenario 8 o custom si mayor) // Interfaz unificada (PHASE 2 + PHASE 3) void switchToTheme(int theme_index); // Cambia a tema 0-14 con transición LERP suave (PHASE 3) @@ -99,6 +100,9 @@ class ThemeManager { // Snapshot del tema origen (capturado al iniciar transición) std::unique_ptr source_snapshot_; // nullptr si no hay transición + // Máximo de bolas posible en esta sesión (max(SCENE_BALLS_8, custom_balls)) + int max_ball_count_ = SCENE_BALLS_8; + // ======================================== // MÉTODOS PRIVADOS // ========================================