refactor: eliminar sistema de shaders externos (ShaderManager + GpuShaderPreset)

Elimina el sistema multi-pass de shaders runtime en favor del PostFX nativo.
Queda solo el ciclo de 5 modos nativos: OFF → Vinyeta → Scanlines → Cromàtica → Complet.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-20 17:08:08 +01:00
parent 8dde13409b
commit c052b45a60
24 changed files with 20 additions and 1512 deletions

View File

@@ -354,26 +354,6 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen, AppMod
delete[] tmp;
}
// Inicializar ShaderManager (shaders externos desde data/shaders/)
if (gpu_ctx_ && success) {
shader_manager_ = std::make_unique<ShaderManager>();
std::string shaders_dir = getResourcesDirectory() + "/data/shaders";
shader_manager_->scan(shaders_dir);
// Si se especificó --shader, activar el preset inicial
if (!initial_shader_name_.empty()) {
active_shader_ = shader_manager_->load(
gpu_ctx_->device(),
initial_shader_name_,
gpu_ctx_->swapchainFormat(),
current_screen_width_, current_screen_height_);
if (active_shader_) {
const auto& names = shader_manager_->names();
auto it = std::find(names.begin(), names.end(), initial_shader_name_);
active_shader_idx_ = (it != names.end()) ? (int)(it - names.begin()) : -1;
}
}
}
}
return success;
@@ -408,7 +388,6 @@ void Engine::shutdown() {
if (sprite_batch_) { sprite_batch_->destroy(gpu_ctx_->device()); sprite_batch_.reset(); }
if (gpu_ball_buffer_) { gpu_ball_buffer_->destroy(gpu_ctx_->device()); gpu_ball_buffer_.reset(); }
if (gpu_pipeline_) { gpu_pipeline_->destroy(gpu_ctx_->device()); gpu_pipeline_.reset(); }
if (shader_manager_) { shader_manager_->destroyAll(gpu_ctx_->device()); shader_manager_.reset(); }
}
// Destroy software UI renderer and surface
@@ -903,75 +882,7 @@ void Engine::render() {
SDL_SetGPUScissor(rp, &scissor);
};
if (active_shader_ != nullptr) {
// --- External multi-pass shader ---
NTSCParams ntsc = {};
ntsc.source_width = static_cast<float>(current_screen_width_);
ntsc.source_height = static_cast<float>(current_screen_height_);
ntsc.a_value = active_shader_->param("avalue", 0.0f);
ntsc.b_value = active_shader_->param("bvalue", 0.0f);
ntsc.cc_value = active_shader_->param("ccvalue", 3.5795455f);
ntsc.scan_time = active_shader_->param("scantime", 47.9f);
ntsc.notch_width = active_shader_->param("notch_width", 3.45f);
ntsc.y_freq = active_shader_->param("yfreqresponse", 1.75f);
ntsc.i_freq = active_shader_->param("ifreqresponse", 1.75f);
ntsc.q_freq = active_shader_->param("qfreqresponse", 1.45f);
SDL_GPUTexture* current_input = offscreen_tex_->texture();
SDL_GPUSampler* current_samp = offscreen_tex_->sampler();
for (int pi = 0; pi < active_shader_->passCount(); ++pi) {
ShaderPass& sp = active_shader_->pass(pi);
bool is_last = (pi == active_shader_->passCount() - 1);
SDL_GPUTexture* target_tex = is_last ? swapchain : sp.target->texture();
SDL_GPULoadOp load_op = SDL_GPU_LOADOP_CLEAR;
SDL_GPUColorTargetInfo ct = {};
ct.texture = target_tex;
ct.load_op = load_op;
ct.clear_color = {0.0f, 0.0f, 0.0f, 1.0f};
ct.store_op = SDL_GPU_STOREOP_STORE;
SDL_GPURenderPass* ext_pass = SDL_BeginGPURenderPass(cmd, &ct, 1, nullptr);
if (is_last) applyViewport(ext_pass);
SDL_BindGPUGraphicsPipeline(ext_pass, sp.pipeline);
SDL_GPUTextureSamplerBinding src_tsb = {current_input, current_samp};
SDL_BindGPUFragmentSamplers(ext_pass, 0, &src_tsb, 1);
SDL_PushGPUFragmentUniformData(cmd, 0, &ntsc, sizeof(NTSCParams));
SDL_DrawGPUPrimitives(ext_pass, 3, 1, 0, 0);
SDL_EndGPURenderPass(ext_pass);
if (!is_last) {
current_input = sp.target->texture();
current_samp = sp.target->sampler();
}
}
// Re-open swapchain pass for UI overlay
SDL_GPUColorTargetInfo ct_ui = {};
ct_ui.texture = swapchain;
ct_ui.load_op = SDL_GPU_LOADOP_LOAD; // preserve shader output
ct_ui.store_op = SDL_GPU_STOREOP_STORE;
SDL_GPURenderPass* pass2 = SDL_BeginGPURenderPass(cmd, &ct_ui, 1, nullptr);
applyViewport(pass2);
if (ui_tex_ && ui_tex_->isValid() && sprite_batch_->overlayIndexCount() > 0) {
SDL_BindGPUGraphicsPipeline(pass2, gpu_pipeline_->spritePipeline());
SDL_GPUBufferBinding vb = {sprite_batch_->vertexBuffer(), 0};
SDL_GPUBufferBinding ib = {sprite_batch_->indexBuffer(), 0};
SDL_BindGPUVertexBuffers(pass2, 0, &vb, 1);
SDL_BindGPUIndexBuffer(pass2, &ib, SDL_GPU_INDEXELEMENTSIZE_32BIT);
SDL_GPUTextureSamplerBinding ui_tsb = {ui_tex_->texture(), ui_tex_->sampler()};
SDL_BindGPUFragmentSamplers(pass2, 0, &ui_tsb, 1);
SDL_DrawGPUIndexedPrimitives(pass2, sprite_batch_->overlayIndexCount(), 1,
sprite_batch_->overlayIndexOffset(), 0, 0);
}
SDL_EndGPURenderPass(pass2);
} else {
{
// --- Native PostFX path ---
SDL_GPUColorTargetInfo ct = {};
ct.texture = swapchain;
@@ -1003,7 +914,7 @@ void Engine::render() {
}
SDL_EndGPURenderPass(pass2);
} // end else (native PostFX)
} // end native PostFX
} // end if (swapchain && ...)
gpu_ctx_->submit(cmd);
@@ -1155,7 +1066,6 @@ void Engine::applyPostFXPreset(int mode) {
}
void Engine::handlePostFXCycle() {
// Delegate to cycleShader() which handles both native PostFX and external shaders
cycleShader();
}
@@ -1164,16 +1074,6 @@ void Engine::handlePostFXToggle() {
"PostFX: Vinyeta", "PostFX: Scanlines",
"PostFX: Cromàtica", "PostFX: Complet"
};
// If external shader is active, toggle it off
if (active_shader_) {
active_shader_ = nullptr;
active_shader_idx_ = 0; // reset to OFF
postfx_uniforms_.vignette_strength = 0.0f;
postfx_uniforms_.chroma_strength = 0.0f;
postfx_uniforms_.scanline_strength = 0.0f;
showNotificationForAction("PostFX: Desactivat");
return;
}
postfx_enabled_ = !postfx_enabled_;
if (postfx_enabled_) {
applyPostFXPreset(postfx_effect_mode_);
@@ -1201,81 +1101,25 @@ void Engine::setPostFXParamOverrides(float vignette, float chroma) {
if (chroma >= 0.f) postfx_uniforms_.chroma_strength = chroma;
}
void Engine::setInitialShader(const std::string& name) {
initial_shader_name_ = name;
}
void Engine::cycleShader() {
// Cycle order:
// native OFF → native Vinyeta → Scanlines → Cromàtica → Complet →
// ext shader 0 → ext shader 1 → ... → native OFF → ...
if (!shader_manager_) {
// No shader manager: fall back to native PostFX cycle
handlePostFXCycle();
return;
}
// active_shader_idx_ is a 0-based cycle counter:
// -1 = uninitialized (first press → index 0 = OFF)
// 0 = OFF
// 1 = PostFX Vinyeta, 2 = Scanlines, 3 = Cromàtica, 4 = Complet
// 5..4+num_ext = external shaders (0-based into names())
const int num_native = 5; // 0=OFF, 1..4=PostFX modes
const int num_ext = static_cast<int>(shader_manager_->names().size());
const int total = num_native + num_ext;
static const char* native_names[5] = {
static const char* names[5] = {
"PostFX: Desactivat", "PostFX: Vinyeta", "PostFX: Scanlines",
"PostFX: Cromàtica", "PostFX: Complet"
};
postfx_cycle_idx_ = (postfx_cycle_idx_ + 1) % 5;
int cycle = postfx_cycle_idx_;
// Advance and wrap
int cycle = active_shader_idx_ + 1;
if (cycle < 0 || cycle >= total) cycle = 0;
active_shader_idx_ = cycle;
if (cycle < num_native) {
// Native PostFX
active_shader_ = nullptr;
if (cycle == 0) {
postfx_enabled_ = false;
postfx_uniforms_.vignette_strength = 0.0f;
postfx_uniforms_.chroma_strength = 0.0f;
postfx_uniforms_.scanline_strength = 0.0f;
} else {
postfx_enabled_ = true;
postfx_effect_mode_ = cycle - 1; // 0..3
applyPostFXPreset(postfx_effect_mode_);
}
showNotificationForAction(native_names[cycle]);
if (cycle == 0) {
postfx_enabled_ = false;
postfx_uniforms_.vignette_strength = 0.0f;
postfx_uniforms_.chroma_strength = 0.0f;
postfx_uniforms_.scanline_strength = 0.0f;
} else {
// External shader
int ext_idx = cycle - num_native;
const std::string& shader_name = shader_manager_->names()[ext_idx];
GpuShaderPreset* preset = shader_manager_->load(
gpu_ctx_->device(),
shader_name,
gpu_ctx_->swapchainFormat(),
current_screen_width_, current_screen_height_);
if (preset) {
active_shader_ = preset;
postfx_enabled_ = false;
postfx_uniforms_.vignette_strength = 0.0f;
postfx_uniforms_.chroma_strength = 0.0f;
postfx_uniforms_.scanline_strength = 0.0f;
showNotificationForAction("Shader: " + shader_name);
} else {
// Failed to load: skip to next
SDL_Log("Engine::cycleShader: failed to load '%s', skipping", shader_name.c_str());
active_shader_ = nullptr;
showNotificationForAction("Shader: ERROR " + shader_name);
}
postfx_enabled_ = true;
postfx_effect_mode_ = cycle - 1;
applyPostFXPreset(postfx_effect_mode_);
}
}
std::string Engine::getActiveShaderName() const {
if (active_shader_) return active_shader_->name();
return {};
showNotificationForAction(names[cycle]);
}
void Engine::toggleIntegerScaling() {
@@ -1621,11 +1465,6 @@ void Engine::recreateOffscreenTexture() {
base_screen_width_, base_screen_height_); // logical (font size based on base)
}
// Recreate external shader intermediate targets
if (shader_manager_) {
shader_manager_->onResize(gpu_ctx_->device(), gpu_ctx_->swapchainFormat(),
current_screen_width_, current_screen_height_);
}
if (ui_renderer_ && app_logo_) {
app_logo_->initialize(ui_renderer_, current_screen_width_, current_screen_height_);
}