diff --git a/data/lang/ba_BA.json b/data/lang/ba_BA.json index 4523241..5c220d8 100644 --- a/data/lang/ba_BA.json +++ b/data/lang/ba_BA.json @@ -86,6 +86,7 @@ "[SERVICE_MENU] SUPERSAMPLING": "Supermostreig", "[SERVICE_MENU] VSYNC": "Sincronisme vertical", "[SERVICE_MENU] INTEGER_SCALE": "Escalat sencer", + "[SERVICE_MENU] FILTER": "Filtre", "[SERVICE_MENU] MAIN_VOLUME": "Volumen general", "[SERVICE_MENU] MUSIC_VOLUME": "Volumen de la musica", "[SERVICE_MENU] SFX_VOLUME": "Volumen dels sons", diff --git a/data/lang/en_UK.json b/data/lang/en_UK.json index 85d7d2c..977b6fb 100644 --- a/data/lang/en_UK.json +++ b/data/lang/en_UK.json @@ -85,6 +85,7 @@ "[SERVICE_MENU] SUPERSAMPLING": "Supersampling", "[SERVICE_MENU] VSYNC": "V-Sync", "[SERVICE_MENU] INTEGER_SCALE": "Integer Scale", + "[SERVICE_MENU] FILTER": "Filter", "[SERVICE_MENU] MAIN_VOLUME": "Main Volume", "[SERVICE_MENU] MUSIC_VOLUME": "Music Volume", "[SERVICE_MENU] SFX_VOLUME": "Sound Volume", diff --git a/data/lang/es_ES.json b/data/lang/es_ES.json index dc2236c..4168c4a 100644 --- a/data/lang/es_ES.json +++ b/data/lang/es_ES.json @@ -85,6 +85,7 @@ "[SERVICE_MENU] SUPERSAMPLING": "Supersampling", "[SERVICE_MENU] VSYNC": "Sincronismo vertical", "[SERVICE_MENU] INTEGER_SCALE": "Escalado proporcional", + "[SERVICE_MENU] FILTER": "Filtro", "[SERVICE_MENU] MAIN_VOLUME": "Volumen general", "[SERVICE_MENU] MUSIC_VOLUME": "Volumen de la musica", "[SERVICE_MENU] SFX_VOLUME": "Volumen de los efectos", diff --git a/source/core/rendering/screen.cpp b/source/core/rendering/screen.cpp index bc87805..0120b38 100644 --- a/source/core/rendering/screen.cpp +++ b/source/core/rendering/screen.cpp @@ -94,7 +94,7 @@ Screen::Screen() // Crea la textura de destino game_canvas_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height); - SDL_SetTextureScaleMode(game_canvas_, SDL_SCALEMODE_NEAREST); + SDL_SetTextureScaleMode(game_canvas_, Options::video.scale_mode); // Inicializar buffer de píxeles para SDL3GPU pixel_buffer_.resize(static_cast(param.game.width) * static_cast(param.game.height)); @@ -670,6 +670,25 @@ void Screen::toggleVSync() { } } +// Aplica Options::video.scale_mode a la textura del canvas de juego +void Screen::applyFilter() { + SDL_SetTextureScaleMode(game_canvas_, Options::video.scale_mode); +} + +// Alterna el modo de filtrado entre nearest y linear +void Screen::toggleFilter() { + Options::video.scale_mode = (Options::video.scale_mode == SDL_SCALEMODE_NEAREST) + ? SDL_SCALEMODE_LINEAR + : SDL_SCALEMODE_NEAREST; + applyFilter(); +} + +// Devuelve true si el backend SDL3GPU está activo y con aceleración hardware +auto Screen::isHardwareAccelerated() -> bool { + auto* self = Screen::get(); + return self != nullptr && self->shader_backend_ && self->shader_backend_->isHardwareAccelerated(); +} + // Establece el estado del V-Sync void Screen::setVSync(bool enabled) { Options::video.vsync = enabled; diff --git a/source/core/rendering/screen.hpp b/source/core/rendering/screen.hpp index bd18126..4d55c9f 100644 --- a/source/core/rendering/screen.hpp +++ b/source/core/rendering/screen.hpp @@ -49,6 +49,8 @@ class Screen { static void nextPostFXPreset(); // Avanza al siguiente preset PostFX static void nextCrtPiPreset(); // Avanza al siguiente preset CrtPi static void toggleSupersampling(); // Alterna supersampling + void toggleFilter(); // Alterna SDL_SCALEMODE_NEAREST ↔ SDL_SCALEMODE_LINEAR + void applyFilter(); // Aplica Options::video.scale_mode a game_canvas_ void toggleIntegerScale(); void toggleVSync(); // Alterna entre activar y desactivar el V-Sync void setVSync(bool enabled); // Establece el estado del V-Sync @@ -59,7 +61,8 @@ class Screen { void show() { SDL_ShowWindow(window_); } // Muestra la ventana void hide() { SDL_HideWindow(window_); } // Oculta la ventana void getSingletons(); // Obtiene los punteros a los singletones - [[nodiscard]] static auto getVSync() -> bool { return Options::video.vsync; } // Obtiene el valor de V-Sync + [[nodiscard]] static auto getVSync() -> bool { return Options::video.vsync; } // Obtiene el valor de V-Sync + [[nodiscard]] static auto isHardwareAccelerated() -> bool; // True si SDL3GPU está activo [[nodiscard]] auto getText() const -> std::shared_ptr { return text_; } // Obtiene el puntero al texto de Screen // --- Display Monitor getters --- diff --git a/source/game/ui/service_menu.cpp b/source/game/ui/service_menu.cpp index ef32168..1b6080d 100644 --- a/source/game/ui/service_menu.cpp +++ b/source/game/ui/service_menu.cpp @@ -466,6 +466,24 @@ void ServiceMenu::initializeOptions() { SettingsGroup::VIDEO, &Options::video.integer_scale)); + // FILTER: Nearest / Linear (solo visible en el fallback SDL, sin GPU acelerada) + { + std::vector filter_values = {"Nearest", "Linear"}; + auto filter_getter = []() -> std::string { + return (Options::video.scale_mode == SDL_SCALEMODE_LINEAR) ? "Linear" : "Nearest"; + }; + auto filter_setter = [](const std::string& val) { + Options::video.scale_mode = (val == "Linear") ? SDL_SCALEMODE_LINEAR : SDL_SCALEMODE_NEAREST; + if (Screen::get() != nullptr) { Screen::get()->applyFilter(); } + }; + options_.push_back(std::make_unique( + Lang::getText("[SERVICE_MENU] FILTER"), + SettingsGroup::VIDEO, + filter_values, + filter_getter, + filter_setter)); + } + // AUDIO options_.push_back(std::make_unique( Lang::getText("[SERVICE_MENU] AUDIO"), @@ -644,26 +662,44 @@ void ServiceMenu::setHiddenOptions() { } } + // Las opciones de shader solo tienen sentido cuando SDL3GPU está activo. + // Las opciones de filtro SDL solo tienen sentido en el fallback sin GPU. + // Ambos checks son runtime para cubrir tanto builds sin shaders como fallos de inicialización. + const bool HW_ACCEL = Screen::isHardwareAccelerated(); + + { + auto* option = getOptionByCaption(Lang::getText("[SERVICE_MENU] FILTER")); + if (option != nullptr) { + option->setHidden(HW_ACCEL); + } + } + + { + auto* option = getOptionByCaption(Lang::getText("[SERVICE_MENU] SHADER")); + if (option != nullptr) { + option->setHidden(!HW_ACCEL); + } + } + { auto* option = getOptionByCaption(Lang::getText("[SERVICE_MENU] SHADER_PRESET")); if (option != nullptr) { - option->setHidden(!Options::video.shader.enabled); + option->setHidden(!HW_ACCEL || !Options::video.shader.enabled); } } { auto* option = getOptionByCaption(Lang::getText("[SERVICE_MENU] SUPERSAMPLING")); if (option != nullptr) { - option->setHidden(!Options::video.shader.enabled || Options::video.shader.current_shader != Rendering::ShaderType::POSTFX); + option->setHidden(!HW_ACCEL || !Options::video.shader.enabled || Options::video.shader.current_shader != Rendering::ShaderType::POSTFX); } } #ifdef __EMSCRIPTEN__ - // En la versión web no tiene sentido exponer: shaders (build sin shaders), - // permitir apagar el sistema (no aplica al navegador) ni salir del juego - // (lo gestiona el navegador). + // En la versión web no tiene sentido exponer: permitir apagar el sistema + // (no aplica al navegador) ni salir del juego (lo gestiona el navegador). + // SHADER ya queda oculto por el check de HW_ACCEL de arriba. for (const auto& caption : { - Lang::getText("[SERVICE_MENU] SHADER"), Lang::getText("[SERVICE_MENU] ENABLE_SHUTDOWN"), Lang::getText("[SERVICE_MENU] QUIT")}) { auto* option = getOptionByCaption(caption);