refactor: constantes SCENE_BALLS_N y fix HUD overlay en SHAPE mode

- defines.hpp: añadir SCENE_BALLS_1..8 (topado en 50K), SCENARIO_COUNT,
  reconstruir BALL_COUNT_SCENARIOS con esas constantes
- theme_manager: añadir max_ball_count_ y setMaxBallCount() para capturar
  colores hasta el máximo real (custom incluido), eliminando literal 50000
- engine.cpp: llamar setMaxBallCount() tras inicializar ThemeManager
- gpu_sprite_batch: addFullscreenOverlay() escribe vértices directamente
  sin pasar por el guard de pushQuad(), igual que addBackground(); esto
  corrige el HUD/overlay invisible en SHAPE mode con escenario 8 (50K bolas)
- Textos UI actualizados: tecla 8, help overlay y --skip-benchmark → 50.000

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-21 00:55:57 +01:00
parent 093b982e01
commit e1f6fd0f39
5 changed files with 46 additions and 16 deletions

View File

@@ -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) // Escenarios de número de pelotas (teclas 1-8)
// Fase 1 (instanced rendering): límit pràctic ~100K a 60fps (physics bound) // 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) // 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 // 0 1 2 3 4 5 6 7
constexpr int DEMO_AUTO_MIN_SCENARIO = 2; // mínimo 100 bolas 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) constexpr int DEMO_AUTO_MAX_SCENARIO = 7; // máximo sin restricción hardware (ajustado por benchmark)

View File

@@ -282,6 +282,12 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen, AppMod
// Inicializar ThemeManager PRIMERO (requerido por Notifier y SceneManager) // Inicializar ThemeManager PRIMERO (requerido por Notifier y SceneManager)
theme_manager_ = std::make_unique<ThemeManager>(); theme_manager_ = std::make_unique<ThemeManager>();
theme_manager_->initialize(); 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) // Inicializar SceneManager (gestión de bolas y física)
scene_manager_ = std::make_unique<SceneManager>(current_screen_width_, current_screen_height_); scene_manager_ = std::make_unique<SceneManager>(current_screen_width_, current_screen_height_);

View File

@@ -9,10 +9,10 @@
bool GpuSpriteBatch::init(SDL_GPUDevice* device, int max_sprites) { bool GpuSpriteBatch::init(SDL_GPUDevice* device, int max_sprites) {
max_sprites_ = max_sprites; max_sprites_ = max_sprites;
// Pre-allocate GPU buffers large enough for (max_sprites_ + 1) quads. // Pre-allocate GPU buffers large enough for (max_sprites_ + 2) quads.
// The +1 reserves a guaranteed slot for the fullscreen overlay. // The +2 reserves one slot for the background quad and one for the fullscreen overlay.
Uint32 max_verts = static_cast<Uint32>(max_sprites_ + 1) * 4; Uint32 max_verts = static_cast<Uint32>(max_sprites_ + 2) * 4;
Uint32 max_indices = static_cast<Uint32>(max_sprites_ + 1) * 6; Uint32 max_indices = static_cast<Uint32>(max_sprites_ + 2) * 6;
Uint32 vb_size = max_verts * sizeof(GpuVertex); Uint32 vb_size = max_verts * sizeof(GpuVertex);
Uint32 ib_size = max_indices * sizeof(uint32_t); Uint32 ib_size = max_indices * sizeof(uint32_t);
@@ -55,8 +55,8 @@ bool GpuSpriteBatch::init(SDL_GPUDevice* device, int max_sprites) {
return false; return false;
} }
vertices_.reserve(static_cast<size_t>(max_sprites_ + 1) * 4); vertices_.reserve(static_cast<size_t>(max_sprites_ + 2) * 4);
indices_.reserve(static_cast<size_t>(max_sprites_ + 1) * 6); indices_.reserve(static_cast<size_t>(max_sprites_ + 2) * 6);
return true; return true;
} }
@@ -130,10 +130,17 @@ void GpuSpriteBatch::addSprite(float x, float y, float w, float h,
} }
void GpuSpriteBatch::addFullscreenOverlay() { 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<int>(indices_.size()); overlay_index_offset_ = static_cast<int>(indices_.size());
size_t before = indices_.size(); uint32_t vi = static_cast<uint32_t>(vertices_.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); vertices_.push_back({ -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f });
overlay_index_count_ = static_cast<int>(indices_.size() - before); // 0 si pushQuad falló, 6 si ok 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) { 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, void GpuSpriteBatch::pushQuad(float ndx0, float ndy0, float ndx1, float ndy1,
float u0, float v0, float u1, float v1, float u0, float v0, float u1, float v1,
float r, float g, float b, float a) { float r, float g, float b, float a) {
if (vertices_.size() + 4 > static_cast<size_t>(max_sprites_) * 4) return; // +1 reserva el slot del background que ya entró sin pasar por este guard.
if (vertices_.size() + 4 > static_cast<size_t>(max_sprites_ + 1) * 4) return;
uint32_t vi = static_cast<uint32_t>(vertices_.size()); uint32_t vi = static_cast<uint32_t>(vertices_.size());
// TL, TR, BR, BL // TL, TR, BR, BL

View File

@@ -640,10 +640,10 @@ std::unique_ptr<ThemeSnapshot> ThemeManager::captureCurrentSnapshot() const {
snapshot->name_en = themes_[current_theme_index_]->getNameEN(); snapshot->name_en = themes_[current_theme_index_]->getNameEN();
snapshot->name_es = themes_[current_theme_index_]->getNameES(); snapshot->name_es = themes_[current_theme_index_]->getNameES();
// Capturar colores de pelotas (suficientes para escenario máximo: 50,000) // Capturar colores de pelotas para el máximo real de esta sesión
// Esto asegura LERP correcto incluso en escenarios grandes // (SCENE_BALLS_8 o más si hay escenario custom)
snapshot->ball_colors.reserve(50000); snapshot->ball_colors.reserve(max_ball_count_);
for (size_t i = 0; i < 50000; i++) { for (int i = 0; i < max_ball_count_; i++) {
snapshot->ball_colors.push_back( snapshot->ball_colors.push_back(
themes_[current_theme_index_]->getBallColor(i, 0.0f) themes_[current_theme_index_]->getBallColor(i, 0.0f)
); );

View File

@@ -43,6 +43,7 @@ class ThemeManager {
// Inicialización // Inicialización
void initialize(); // Inicializa 15 temas unificados (9 estáticos + 6 dinámicos) 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) // Interfaz unificada (PHASE 2 + PHASE 3)
void switchToTheme(int theme_index); // Cambia a tema 0-14 con transición LERP suave (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) // Snapshot del tema origen (capturado al iniciar transición)
std::unique_ptr<ThemeSnapshot> source_snapshot_; // nullptr si no hay transición std::unique_ptr<ThemeSnapshot> 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 // MÉTODOS PRIVADOS
// ======================================== // ========================================