diff --git a/source/core/rendering/screen.cpp b/source/core/rendering/screen.cpp index 1daba98..cf05816 100644 --- a/source/core/rendering/screen.cpp +++ b/source/core/rendering/screen.cpp @@ -89,6 +89,10 @@ Screen::Screen() border_surface_->setPalette(initial_palette); border_surface_->clear(border_color_); + // Cachear el color ARGB inicial del borde (borde sólido por defecto) + border_surface_->toARGBBuffer(border_pixel_buffer_.data()); + border_argb_color_ = border_pixel_buffer_[0]; + // Establece la surface que actuará como renderer para recibir las llamadas a render() renderer_surface_ = std::make_shared>(game_surface_); @@ -192,6 +196,11 @@ auto Screen::incWindowZoom() -> bool { void Screen::setBorderColor(Uint8 color) { border_color_ = color; border_surface_->clear(border_color_); + + // Actualizar caché ARGB del borde sólido (ocurre una vez por habitación, no cada frame) + border_surface_->toARGBBuffer(border_pixel_buffer_.data()); + border_argb_color_ = border_pixel_buffer_[0]; + border_is_solid_ = true; } // Cambia entre borde visible y no visible @@ -317,6 +326,12 @@ void Screen::setPalete() { // NOLINT(readability-convert-member-functions-to-st // Convertir a mayúsculas std::ranges::transform(Options::video.palette, Options::video.palette.begin(), ::toupper); + + // Actualizar caché si el borde es sólido (la paleta cambia el valor ARGB del color) + if (border_is_solid_) { + border_surface_->toARGBBuffer(border_pixel_buffer_.data()); + border_argb_color_ = border_pixel_buffer_[0]; + } } // Extrae los nombres de las paletas @@ -343,30 +358,41 @@ void Screen::textureToRenderer() { const int GAME_H = Options::game.height; if (Options::video.border.enabled) { - // 1. Volcamos la Surface del borde al buffer (aquí van las franjas de carga) - // Esto es mucho más rápido que un bucle manual - border_surface_->toARGBBuffer(border_pixel_buffer_.data()); - - // 2. Extraemos los píxeles del juego - game_surface_->toARGBBuffer(game_pixel_buffer_.data()); - - // 3. Superponemos el juego sobre el borde (Composición por filas) const int BORDER_W = window_width_; + const int BORDER_H = window_height_; const int OFF_X = static_cast(game_surface_dstrect_.x); const int OFF_Y = static_cast(game_surface_dstrect_.y); - for (int y = 0; y < GAME_H; ++y) { - // Puntero de origen (fila Y del juego) - const Uint32* src = &game_pixel_buffer_[y * GAME_W]; - // Puntero de destino (fila Y + offset en el buffer global) - Uint32* dst = &border_pixel_buffer_[(OFF_Y + y) * BORDER_W + OFF_X]; + if (border_is_solid_) { + // Path A: borde sólido (gameplay normal) + // Rellena solo el marco con el color cacheado — sin lookups de paleta. + // El área central (juego) se deja sin tocar; el overlay la sobreescribe igualmente. - // Copia de bloque de memoria (muy eficiente) + // Franjas superior e inferior (ancho completo) + std::fill_n(border_pixel_buffer_.data(), OFF_Y * BORDER_W, border_argb_color_); + std::fill_n(&border_pixel_buffer_[(OFF_Y + GAME_H) * BORDER_W], + (BORDER_H - OFF_Y - GAME_H) * BORDER_W, border_argb_color_); + // Columnas laterales en las filas del área de juego + for (int y = OFF_Y; y < OFF_Y + GAME_H; ++y) { + std::fill_n(&border_pixel_buffer_[y * BORDER_W], OFF_X, border_argb_color_); + std::fill_n(&border_pixel_buffer_[y * BORDER_W + OFF_X + GAME_W], + BORDER_W - OFF_X - GAME_W, border_argb_color_); + } + } else { + // Path B: borde dinámico (escena de carga — bandas de colores animadas) + // Conversión completa: la escena modifica border_surface_ cada frame + border_surface_->toARGBBuffer(border_pixel_buffer_.data()); + } + + // Overlay del juego sobre el centro del buffer (ambos paths) + game_surface_->toARGBBuffer(game_pixel_buffer_.data()); + for (int y = 0; y < GAME_H; ++y) { + const Uint32* src = &game_pixel_buffer_[y * GAME_W]; + Uint32* dst = &border_pixel_buffer_[(OFF_Y + y) * BORDER_W + OFF_X]; std::memcpy(dst, src, GAME_W * sizeof(Uint32)); } - // 4. Subida final de un único buffer completo - shader_backend_->uploadPixels(border_pixel_buffer_.data(), BORDER_W, window_height_); + shader_backend_->uploadPixels(border_pixel_buffer_.data(), BORDER_W, BORDER_H); } else { // Caso sin borde: subida directa simplificada game_surface_->toARGBBuffer(game_pixel_buffer_.data()); @@ -492,7 +518,10 @@ void Screen::toggleVSync() { // Getters auto Screen::getRenderer() -> SDL_Renderer* { return renderer_; } auto Screen::getRendererSurface() -> std::shared_ptr { return (*renderer_surface_); } -auto Screen::getBorderSurface() -> std::shared_ptr { return border_surface_; } +auto Screen::getBorderSurface() -> std::shared_ptr { + border_is_solid_ = false; // Modificación externa → modo borde dinámico + return border_surface_; +} auto loadData(const std::string& filepath) -> std::vector { // Load using ResourceHelper (supports both filesystem and pack) diff --git a/source/core/rendering/screen.hpp b/source/core/rendering/screen.hpp index 8189fd4..23afdd3 100644 --- a/source/core/rendering/screen.hpp +++ b/source/core/rendering/screen.hpp @@ -141,7 +141,11 @@ class Screen { // Buffers persistentes para evitar .resize() cada frame std::vector game_pixel_buffer_; // Textura de juego - std::vector border_pixel_buffer_; // Textura de borde + std::vector border_pixel_buffer_; // Textura de borde (composición final borde+juego) + + // Caché del borde sólido (gameplay normal) + bool border_is_solid_{true}; // true = borde de color sólido; false = borde dinámico (carga) + Uint32 border_argb_color_{0}; // Color ARGB pre-convertido del borde sólido // Configuración de ventana y pantalla int window_width_{0}; // Ancho de la pantalla o ventana @@ -159,9 +163,8 @@ class Screen { DisplayMonitor display_monitor_; // Información de la pantalla // Shaders - std::string info_resolution_; // Texto con la información de la pantalla - std::string gpu_driver_; // Nombre del driver GPU (SDL3GPU), capturado en initShaders() - std::vector pixel_buffer_; // Buffer intermedio para SDL3GPU path (surface → ARGB) + std::string info_resolution_; // Texto con la información de la pantalla + std::string gpu_driver_; // Nombre del driver GPU (SDL3GPU), capturado en initShaders() #ifdef _DEBUG bool show_fps_{true}; // Indica si ha de mostrar el contador de FPS