supersampling implementat

This commit is contained in:
2026-03-22 21:55:18 +01:00
parent c87779cc09
commit 1ecb427106
10 changed files with 67 additions and 32 deletions

View File

@@ -111,6 +111,8 @@ ui:
postfx_enabled: "POSTFX ACTIVAT"
postfx_disabled: "POSTFX DESACTIVAT"
postfx: "POSTFX"
supersampling_enabled: "SUPERMOSTREIG ACTIVAT"
supersampling_disabled: "SUPERMOSTREIG DESACTIVAT"
palette: "PALETA"
integer_scale_enabled: "ESCALAT SENCER ACTIVAT"
integer_scale_disabled: "ESCALAT SENCER DESACTIVAT"

View File

@@ -111,6 +111,8 @@ ui:
postfx_enabled: "POSTFX ENABLED"
postfx_disabled: "POSTFX DISABLED"
postfx: "POSTFX"
supersampling_enabled: "SUPERSAMPLING ON"
supersampling_disabled: "SUPERSAMPLING OFF"
palette: "PALETTE"
integer_scale_enabled: "INTEGER SCALE ENABLED"
integer_scale_disabled: "INTEGER SCALE DISABLED"

View File

@@ -93,6 +93,11 @@ namespace GlobalInputs {
Notifier::get()->show({Locale::get()->get(Options::video.postfx ? "ui.postfx_enabled" : "ui.postfx_disabled")});
}
void handleToggleSupersampling() {
Screen::get()->toggleSupersampling();
Notifier::get()->show({Locale::get()->get(Options::video.supersampling ? "ui.supersampling_enabled" : "ui.supersampling_disabled")});
}
void handleNextPostFXPreset() {
if (!Options::postfx_presets.empty()) {
Options::current_postfx_preset = (Options::current_postfx_preset + 1) % static_cast<int>(Options::postfx_presets.size());
@@ -146,10 +151,13 @@ namespace GlobalInputs {
}
}
if (Input::get()->checkAction(InputAction::TOGGLE_POSTFX, Input::DO_NOT_ALLOW_REPEAT)) {
if (Options::video.postfx && ((SDL_GetModState() & SDL_KMOD_SHIFT) != 0U)) {
return InputAction::NEXT_POSTFX_PRESET;
if ((SDL_GetModState() & SDL_KMOD_CTRL) != 0U) {
return InputAction::TOGGLE_SUPERSAMPLING; // Ctrl+F4
}
return InputAction::TOGGLE_POSTFX;
if (Options::video.postfx && ((SDL_GetModState() & SDL_KMOD_SHIFT) != 0U)) {
return InputAction::NEXT_POSTFX_PRESET; // Shift+F4
}
return InputAction::TOGGLE_POSTFX; // F4
}
if (Input::get()->checkAction(InputAction::NEXT_PALETTE, Input::DO_NOT_ALLOW_REPEAT)) {
return InputAction::NEXT_PALETTE;
@@ -225,6 +233,10 @@ namespace GlobalInputs {
handleNextPostFXPreset();
break;
case InputAction::TOGGLE_SUPERSAMPLING:
handleToggleSupersampling();
break;
case InputAction::NEXT_PALETTE:
handleNextPalette();
break;

View File

@@ -26,6 +26,7 @@ enum class InputAction : int { // Acciones de entrada posibles en el juego
TOGGLE_INTEGER_SCALE,
TOGGLE_POSTFX,
NEXT_POSTFX_PRESET,
TOGGLE_SUPERSAMPLING,
TOGGLE_BORDER,
TOGGLE_MUSIC,
NEXT_PALETTE,

View File

@@ -459,14 +459,23 @@ auto loadData(const std::string& filepath) -> std::vector<uint8_t> {
return Resource::Helper::loadFile(filepath);
}
// Activa/desactiva el supersampling global (Ctrl+F4)
void Screen::toggleSupersampling() {
Options::video.supersampling = !Options::video.supersampling;
if (Options::video.postfx && shader_backend_ && shader_backend_->isHardwareAccelerated()) {
applyCurrentPostFXPreset();
}
}
// Aplica los parámetros del preset actual al backend de shaders
void Screen::applyCurrentPostFXPreset() {
if (shader_backend_ && !Options::postfx_presets.empty()) {
const auto& p = Options::postfx_presets[static_cast<size_t>(Options::current_postfx_preset)];
// setOversample primero: puede recrear texturas y debe conocer el factor
// antes de que setPostFXParams decida si hornear scanlines en CPU o GPU.
shader_backend_->setOversample(p.supersampling ? 3 : 1);
Rendering::PostFXParams params{.vignette = p.vignette, .scanlines = p.scanlines, .chroma = p.chroma, .mask = p.mask, .gamma = p.gamma, .curvature = p.curvature, .bleeding = p.bleeding, .supersampling = p.supersampling};
// Supersampling es un toggle global (Options::video.supersampling), no por preset.
// setOversample primero: puede recrear texturas antes de que setPostFXParams
// decida si hornear scanlines en CPU o aplicarlas en GPU.
shader_backend_->setOversample(Options::video.supersampling ? 3 : 1);
Rendering::PostFXParams params{.vignette = p.vignette, .scanlines = p.scanlines, .chroma = p.chroma, .mask = p.mask, .gamma = p.gamma, .curvature = p.curvature, .bleeding = p.bleeding};
shader_backend_->setPostFXParams(params);
}
}

View File

@@ -56,8 +56,9 @@ class Screen {
void nextPalette(); // Cambia a la siguiente paleta
void previousPalette(); // Cambia a la paleta anterior
void setPalete(); // Establece la paleta actual
void togglePostFX(); // Cambia el estado del PostFX
void reloadPostFX(); // Recarga el shader del preset actual sin toggle
void togglePostFX(); // Cambia el estado del PostFX
void toggleSupersampling(); // Activa/desactiva el supersampling global
void reloadPostFX(); // Recarga el shader del preset actual sin toggle
// Surfaces y notificaciones
void setRendererSurface(const std::shared_ptr<Surface>& surface = nullptr); // Establece el renderizador para las surfaces

View File

@@ -18,7 +18,6 @@ namespace Rendering {
float gamma = 0.0F; // Corrección gamma (blend 0=off, 1=full)
float curvature = 0.0F; // Curvatura barrel CRT
float bleeding = 0.0F; // Sangrado de color NTSC
bool supersampling{false}; // Supersampling 3×: scanlines horneadas en CPU + sampler LINEAR
};
/**

View File

@@ -26,6 +26,7 @@ namespace Defaults::Video {
constexpr Screen::Filter FILTER = Screen::Filter::NEAREST; // Filtro por defecto
constexpr bool VERTICAL_SYNC = true; // Vsync activado por defecto
constexpr bool POSTFX = false; // PostFX desactivado por defecto
constexpr bool SUPERSAMPLING = false; // Supersampling desactivado por defecto
constexpr bool INTEGER_SCALE = true; // Escalado entero activado por defecto
constexpr bool KEEP_ASPECT = true; // Mantener aspecto activado por defecto
constexpr const char* PALETTE_NAME = "zx-spectrum"; // Paleta por defecto

View File

@@ -337,6 +337,22 @@ namespace Options {
}
}
if (vid.contains("supersampling")) {
try {
video.supersampling = vid["supersampling"].get_value<bool>();
} catch (...) {
video.supersampling = Defaults::Video::SUPERSAMPLING;
}
}
if (vid.contains("current_postfx_preset")) {
try {
current_postfx_preset = vid["current_postfx_preset"].get_value<int>();
} catch (...) {
current_postfx_preset = 0;
}
}
if (vid.contains("vertical_sync")) {
try {
video.vertical_sync = vid["vertical_sync"].get_value<bool>();
@@ -623,6 +639,8 @@ namespace Options {
file << " fullscreen: " << (video.fullscreen ? "true" : "false") << "\n";
file << " filter: " << filterToString(video.filter) << " # filter: nearest (pixel perfect) | linear (smooth)\n";
file << " postfx: " << (video.postfx ? "true" : "false") << "\n";
file << " supersampling: " << (video.supersampling ? "true" : "false") << "\n";
file << " current_postfx_preset: " << current_postfx_preset << "\n";
file << " vertical_sync: " << (video.vertical_sync ? "true" : "false") << "\n";
file << " integer_scale: " << (video.integer_scale ? "true" : "false") << "\n";
file << " keep_aspect: " << (video.keep_aspect ? "true" : "false") << "\n";
@@ -688,7 +706,6 @@ namespace Options {
// Carga los presets de PostFX desde el fichero
auto loadPostFXFromFile() -> bool {
postfx_presets.clear();
current_postfx_preset = 0;
std::ifstream file(postfx_file_path);
if (!file.good()) {
@@ -718,13 +735,20 @@ namespace Options {
parseFloatField(p, "gamma", preset.gamma);
parseFloatField(p, "curvature", preset.curvature);
parseFloatField(p, "bleeding", preset.bleeding);
if (p.contains("supersampling")) {
try { preset.supersampling = p["supersampling"].get_value<bool>(); } catch (...) {}
}
// Nota: 'supersampling' era un campo por-preset (eliminado). Si existe
// en el fichero del usuario se ignora silenciosamente (compatible).
postfx_presets.push_back(preset);
}
}
// Preservar el índice cargado desde config.yaml; clampar al rango válido.
if (!postfx_presets.empty()) {
current_postfx_preset = std::clamp(
current_postfx_preset, 0, static_cast<int>(postfx_presets.size()) - 1);
} else {
current_postfx_preset = 0;
}
if (console) {
std::cout << "PostFX file loaded: " << postfx_presets.size() << " preset(s)\n";
}
@@ -762,9 +786,7 @@ namespace Options {
file << "# gamma: gamma correction input 2.4 / output 2.2\n";
file << "# curvature: CRT barrel distortion\n";
file << "# bleeding: NTSC horizontal colour bleeding\n";
file << "# supersampling: 3x internal resolution, scanlines baked in CPU + linear filter\n";
file << "# true = consistent 33% scanlines at any zoom (slight softening at non-3x)\n";
file << "# false = sharp pixel art, scanlines depend on zoom (33% at 3x, 25% at 4x)\n";
file << "# Note: supersampling is a global toggle in config.yaml, not per-preset.\n";
file << "\n";
file << "presets:\n";
file << " - name: \"CRT\"\n";
@@ -775,16 +797,6 @@ namespace Options {
file << " gamma: 0.8\n";
file << " curvature: 0.0\n";
file << " bleeding: 0.0\n";
file << " supersampling: false\n";
file << " - name: \"CRT-SS\"\n";
file << " vignette: 0.6\n";
file << " scanlines: 0.7\n";
file << " chroma: 0.15\n";
file << " mask: 0.6\n";
file << " gamma: 0.8\n";
file << " curvature: 0.0\n";
file << " bleeding: 0.0\n";
file << " supersampling: true\n";
file << " - name: \"NTSC\"\n";
file << " vignette: 0.4\n";
file << " scanlines: 0.5\n";
@@ -793,7 +805,6 @@ namespace Options {
file << " gamma: 0.5\n";
file << " curvature: 0.0\n";
file << " bleeding: 0.6\n";
file << " supersampling: false\n";
file << " - name: \"CURVED\"\n";
file << " vignette: 0.5\n";
file << " scanlines: 0.6\n";
@@ -802,7 +813,6 @@ namespace Options {
file << " gamma: 0.7\n";
file << " curvature: 0.8\n";
file << " bleeding: 0.0\n";
file << " supersampling: false\n";
file << " - name: \"SCANLINES\"\n";
file << " vignette: 0.0\n";
file << " scanlines: 0.8\n";
@@ -811,7 +821,6 @@ namespace Options {
file << " gamma: 0.0\n";
file << " curvature: 0.0\n";
file << " bleeding: 0.0\n";
file << " supersampling: false\n";
file << " - name: \"SUBTLE\"\n";
file << " vignette: 0.3\n";
file << " scanlines: 0.4\n";
@@ -820,7 +829,6 @@ namespace Options {
file << " gamma: 0.3\n";
file << " curvature: 0.0\n";
file << " bleeding: 0.0\n";
file << " supersampling: false\n";
file.close();

View File

@@ -81,6 +81,7 @@ namespace Options {
Screen::Filter filter{Defaults::Video::FILTER}; // Filtro usado para el escalado de la imagen
bool vertical_sync{Defaults::Video::VERTICAL_SYNC}; // Indica si se quiere usar vsync o no
bool postfx{Defaults::Video::POSTFX}; // Indica si se van a usar efectos PostFX o no
bool supersampling{Defaults::Video::SUPERSAMPLING}; // Indica si el supersampling 3× está activo
bool integer_scale{Defaults::Video::INTEGER_SCALE}; // Indica si el escalado de la imagen ha de ser entero en el modo a pantalla completa
bool keep_aspect{Defaults::Video::KEEP_ASPECT}; // Indica si se ha de mantener la relación de aspecto al poner el modo a pantalla completa
Border border{}; // Borde de la pantalla
@@ -124,7 +125,6 @@ namespace Options {
float gamma{0.0F}; // Corrección gamma input 2.4 / output 2.2 (0.0 = off, 1.0 = plena)
float curvature{0.0F}; // Distorsión barrel CRT (0.0 = plana, 1.0 = máxima curvatura)
float bleeding{0.0F}; // Sangrado de color NTSC horizontal Y/C (0.0 = off, 1.0 = máximo)
bool supersampling{false}; // 3x supersampling: scanlines horneadas en CPU + sampler LINEAR
};
// --- Variables globales ---