From c052b45a609dece53d5a583902a04941907ac9f7 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Fri, 20 Mar 2026 17:08:08 +0100 Subject: [PATCH] refactor: eliminar sistema de shaders externos (ShaderManager + GpuShaderPreset) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- CMakeLists.txt | 62 ---- README.md | 4 +- data/shaders/ntsc-md-rainbows.slangp | 28 -- .../ntsc-md-rainbows/pass0_encode.frag | 69 ---- .../ntsc-md-rainbows/pass0_encode.frag.spv | Bin 5204 -> 0 bytes .../pass0_encode.frag.spv.msl | 61 ---- .../ntsc-md-rainbows/pass0_encode.vert | 8 - .../ntsc-md-rainbows/pass0_encode.vert.spv | Bin 1436 -> 0 bytes .../pass0_encode.vert.spv.msl | 63 ---- .../ntsc-md-rainbows/pass1_decode.frag | 148 --------- .../ntsc-md-rainbows/pass1_decode.frag.spv | Bin 16192 -> 0 bytes .../pass1_decode.frag.spv.msl | 304 ------------------ .../ntsc-md-rainbows/pass1_decode.vert | 8 - .../ntsc-md-rainbows/pass1_decode.vert.spv | Bin 1436 -> 0 bytes .../pass1_decode.vert.spv.msl | 63 ---- data/shaders/ntsc-md-rainbows/preset.ini | 18 -- source/engine.cpp | 189 +---------- source/engine.hpp | 14 +- source/gpu/gpu_shader_preset.cpp | 274 ---------------- source/gpu/gpu_shader_preset.hpp | 94 ------ source/gpu/shader_manager.cpp | 68 ---- source/gpu/shader_manager.hpp | 41 --- source/main.cpp | 12 - source/ui/ui_manager.cpp | 4 +- 24 files changed, 20 insertions(+), 1512 deletions(-) delete mode 100644 data/shaders/ntsc-md-rainbows.slangp delete mode 100644 data/shaders/ntsc-md-rainbows/pass0_encode.frag delete mode 100644 data/shaders/ntsc-md-rainbows/pass0_encode.frag.spv delete mode 100644 data/shaders/ntsc-md-rainbows/pass0_encode.frag.spv.msl delete mode 100644 data/shaders/ntsc-md-rainbows/pass0_encode.vert delete mode 100644 data/shaders/ntsc-md-rainbows/pass0_encode.vert.spv delete mode 100644 data/shaders/ntsc-md-rainbows/pass0_encode.vert.spv.msl delete mode 100644 data/shaders/ntsc-md-rainbows/pass1_decode.frag delete mode 100644 data/shaders/ntsc-md-rainbows/pass1_decode.frag.spv delete mode 100644 data/shaders/ntsc-md-rainbows/pass1_decode.frag.spv.msl delete mode 100644 data/shaders/ntsc-md-rainbows/pass1_decode.vert delete mode 100644 data/shaders/ntsc-md-rainbows/pass1_decode.vert.spv delete mode 100644 data/shaders/ntsc-md-rainbows/pass1_decode.vert.spv.msl delete mode 100644 data/shaders/ntsc-md-rainbows/preset.ini delete mode 100644 source/gpu/gpu_shader_preset.cpp delete mode 100644 source/gpu/gpu_shader_preset.hpp delete mode 100644 source/gpu/shader_manager.cpp delete mode 100644 source/gpu/shader_manager.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e51365c..f0558ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,61 +64,6 @@ if(NOT APPLE) endforeach() add_custom_target(shaders ALL DEPENDS ${SPIRV_HEADERS}) - - # External runtime shaders: auto-discover and compile data/shaders/**/*.vert/*.frag - # Output: .spv alongside each source file (loaded at runtime by GpuShaderPreset) - file(GLOB_RECURSE DATA_SHADERS - "${CMAKE_SOURCE_DIR}/data/shaders/**/*.vert" - "${CMAKE_SOURCE_DIR}/data/shaders/**/*.frag") - - set(DATA_SHADER_SPVS) - foreach(SHADER_FILE ${DATA_SHADERS}) - get_filename_component(SHADER_EXT "${SHADER_FILE}" EXT) - if(SHADER_EXT STREQUAL ".vert") - set(STAGE_FLAG "-fshader-stage=vertex") - else() - set(STAGE_FLAG "-fshader-stage=fragment") - endif() - set(SPV_FILE "${SHADER_FILE}.spv") - add_custom_command( - OUTPUT "${SPV_FILE}" - COMMAND "${GLSLC}" ${STAGE_FLAG} -o "${SPV_FILE}" "${SHADER_FILE}" - DEPENDS "${SHADER_FILE}" - COMMENT "Compiling ${SHADER_FILE}" - ) - list(APPEND DATA_SHADER_SPVS "${SPV_FILE}") - endforeach() - - if(DATA_SHADER_SPVS) - add_custom_target(data_shaders ALL DEPENDS ${DATA_SHADER_SPVS}) - endif() -endif() - -# macOS: transpile .spv → .msl for GpuShaderPreset -if(APPLE) - find_program(SPIRV_CROSS spirv-cross) - if(SPIRV_CROSS) - file(GLOB_RECURSE DATA_SHADER_SPVS - "${CMAKE_SOURCE_DIR}/data/shaders/*.vert.spv" - "${CMAKE_SOURCE_DIR}/data/shaders/*.frag.spv" - ) - set(DATA_SHADER_MSLS) - foreach(SPV_FILE ${DATA_SHADER_SPVS}) - set(MSL_FILE "${SPV_FILE}.msl") - add_custom_command( - OUTPUT "${MSL_FILE}" - COMMAND "${SPIRV_CROSS}" --msl --output "${MSL_FILE}" "${SPV_FILE}" - DEPENDS "${SPV_FILE}" - COMMENT "Transpiling ${SPV_FILE} to MSL" - ) - list(APPEND DATA_SHADER_MSLS "${MSL_FILE}") - endforeach() - if(DATA_SHADER_MSLS) - add_custom_target(data_shader_msls ALL DEPENDS ${DATA_SHADER_MSLS}) - endif() - else() - message(STATUS "spirv-cross not found — GpuShaderPreset will not work on macOS (run: brew install spirv-cross)") - endif() endif() # Archivos fuente (excluir main_old.cpp) @@ -160,13 +105,6 @@ target_link_libraries(${PROJECT_NAME} ${LINK_LIBS}) if(NOT APPLE) add_dependencies(${PROJECT_NAME} shaders) target_include_directories(${PROJECT_NAME} PRIVATE "${SHADER_GEN_DIR}") - if(TARGET data_shaders) - add_dependencies(${PROJECT_NAME} data_shaders) - endif() -endif() - -if(APPLE AND TARGET data_shader_msls) - add_dependencies(${PROJECT_NAME} data_shader_msls) endif() # Tool: pack_resources diff --git a/README.md b/README.md index 48dc42c..13e2495 100644 --- a/README.md +++ b/README.md @@ -538,7 +538,7 @@ vibe3_physics/ ### Dependencias de shaders -El proyecto compila shaders GLSL a SPIR-V en todas las plataformas usando `glslc` (incluido en el Vulkan SDK). En macOS, adicionalmente transpila los `.spv` a MSL (Metal) con `spirv-cross`. +El proyecto compila shaders GLSL a SPIR-V en todas las plataformas usando `glslc` (incluido en el Vulkan SDK). En macOS, adicionalmente transpila los `.spv` a MSL (Metal) con `spirv-cross` (shaders internos: sprite, ball, postfx). | Plataforma | Backend GPU | Herramienta de shaders | Resultado | |------------|-------------|------------------------|-----------| @@ -624,7 +624,7 @@ cmake .. cmake --build . ``` -En macOS, CMake detecta automáticamente `spirv-cross` y genera los `.spv.msl` necesarios. Si no está instalado, los shaders de preset externos no funcionarán (el resto de la app sí). +En macOS, CMake detecta automáticamente `spirv-cross` y genera los `.spv.msl` necesarios para los shaders internos (sprite, ball, postfx). ### Make directo (Linux/macOS) diff --git a/data/shaders/ntsc-md-rainbows.slangp b/data/shaders/ntsc-md-rainbows.slangp deleted file mode 100644 index d644023..0000000 --- a/data/shaders/ntsc-md-rainbows.slangp +++ /dev/null @@ -1,28 +0,0 @@ -# Based on dannyld's rainbow settings - -shaders = 2 - -shader0 = "../crt/shaders/mame_hlsl/shaders/mame_ntsc_encode.slang" -filter_linear0 = "true" -scale_type0 = "source" -scale0 = "1.000000" - -shader1 = "../crt/shaders/mame_hlsl/shaders/mame_ntsc_decode.slang" -filter_linear1 = "true" -scale_type1 = "source" -scale_1 = "1.000000" - -# ntsc parameters -ntscsignal = "1.000000" -avalue = "0.000000" -bvalue = "0.000000" -scantime = "47.900070" - -# optional blur -shadowalpha = "0.100000" -notch_width = "3.450001" -ifreqresponse = "1.750000" -qfreqresponse = "1.450000" - -# uncomment for jailbars in blue -#pvalue = "1.100000" diff --git a/data/shaders/ntsc-md-rainbows/pass0_encode.frag b/data/shaders/ntsc-md-rainbows/pass0_encode.frag deleted file mode 100644 index 1374e86..0000000 --- a/data/shaders/ntsc-md-rainbows/pass0_encode.frag +++ /dev/null @@ -1,69 +0,0 @@ -#version 450 -// license:BSD-3-Clause -// copyright-holders:Ryan Holtz,ImJezze -// Adapted from mame_ntsc_encode.slang for SDL3 GPU / Vulkan SPIRV - -layout(location=0) in vec2 v_uv; -layout(location=0) out vec4 FragColor; - -layout(set=2, binding=0) uniform sampler2D Source; - -layout(set=3, binding=0) uniform NTSCParams { - float source_width; - float source_height; - float a_value; - float b_value; - float cc_value; - float scan_time; - float notch_width; - float y_freq; - float i_freq; - float q_freq; - float _pad0; - float _pad1; -} u; - -const float PI = 3.1415927; -const float PI2 = PI * 2.0; - -void main() { - vec2 source_dims = vec2(u.source_width, u.source_height); - - // p_value=1: one texel step per sub-sample (no horizontal stretch) - vec2 PValueSourceTexel = vec2(1.0, 0.0) / source_dims; - - vec2 C0 = v_uv + PValueSourceTexel * vec2(0.00, 0.0); - vec2 C1 = v_uv + PValueSourceTexel * vec2(0.25, 0.0); - vec2 C2 = v_uv + PValueSourceTexel * vec2(0.50, 0.0); - vec2 C3 = v_uv + PValueSourceTexel * vec2(0.75, 0.0); - - vec4 Cx = vec4(C0.x, C1.x, C2.x, C3.x); - vec4 Cy = vec4(C0.y, C1.y, C2.y, C3.y); - - vec4 Texel0 = texture(Source, C0); - vec4 Texel1 = texture(Source, C1); - vec4 Texel2 = texture(Source, C2); - vec4 Texel3 = texture(Source, C3); - - vec4 HPosition = Cx; - vec4 VPosition = Cy; - - const vec4 YDot = vec4(0.299, 0.587, 0.114, 0.0); - const vec4 IDot = vec4(0.595716, -0.274453, -0.321263, 0.0); - const vec4 QDot = vec4(0.211456, -0.522591, 0.311135, 0.0); - - vec4 Y = vec4(dot(Texel0, YDot), dot(Texel1, YDot), dot(Texel2, YDot), dot(Texel3, YDot)); - vec4 I = vec4(dot(Texel0, IDot), dot(Texel1, IDot), dot(Texel2, IDot), dot(Texel3, IDot)); - vec4 Q = vec4(dot(Texel0, QDot), dot(Texel1, QDot), dot(Texel2, QDot), dot(Texel3, QDot)); - - float W = PI2 * u.cc_value * u.scan_time; - float WoPI = W / PI; - - float HOffset = u.a_value / WoPI; - float VScale = u.b_value * source_dims.y / WoPI; - - vec4 T = HPosition + vec4(HOffset) + VPosition * vec4(VScale); - vec4 TW = T * W; - - FragColor = Y + I * cos(TW) + Q * sin(TW); -} diff --git a/data/shaders/ntsc-md-rainbows/pass0_encode.frag.spv b/data/shaders/ntsc-md-rainbows/pass0_encode.frag.spv deleted file mode 100644 index 7cfa55216f50137e2a83931c9abd30d1919cfc60..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5204 zcmb7`>vx=06~<3V(o$&~5DC;`O(|YmD=Jj9_GTzaHPB#c0a1rxGEK&&Nt#STqX?~9 zf>`lJK}0RkdMV!VuC-XaftQc2 zm~dh(ah|vspE?VGI;fh`b0l@9m!3Xf% zn5*l%F!MDgYU8sbxuFY3zHV3QO3PVeW^#7Art#nmN*Al8bo1WfzM<-L6^UIfD>UTr z-rX2!j>%)ry_I#w>W$H{W--^Q+w$E?AvVXdMNN;Ps3(*>FntMOgrn&dZRCUCIi`-x2dg{mxVgUaZlXYQc+jy3#0oU}$ot z(QHh5A-w|)#(gi*I?@5O+l%>*L@Q}`i5D1eDe(zL`?1fH;>jUx1wX|&uxJ1NnR>I} zXW;vWYt?ZM)#vaGd~f0HNN2?qvKPM_H%?ba`zFUHr@{SrJtyzg0P|u$>s!O3=ABgY zUWLybHSa@c9i`^EhGvewi!;r8sP>9X^A4(Ak!jvPHP1ilxt>~YrY$cu??U+8i@w)q zn)gp_Bhk-p>~kIAC-_z1j*@%V3_s3$9=Ttx-)G_cdB*TximJYjYSt6?fZS&l!eO79bvkt7^h`O%>v(C2gc%EvxXOotc zwuk4<5VhQMosP6)(X$z*rpL2*2c!G&zS{3Q8SPcwXZEf_=MNuylW#3p%fAh*Kc1QM zeqr6;YJMI3J~}7QKf%{LKG)w@n$&z=a_^g;cs2*Y3q6l*3+_ATsa-vbXEF)@ z`{93X-AHHdV~Q|GpL6;1`562kN3K}Ry$8PL*zt2w`{VEv_nbZ#weE$RqtCg#)~DhB zTDWbouLt4xKi~Ci)cOp(=cbdKX{)c}aC7uIm)Cj({$lqFJIwXoK1z5-!5;_vK8e~- zf?w?3z34v;_b!M3X|Uf%pYd-??6dJ1%g>kEW~!R!T7Jy=H;QZN^}FhO$1@M!)5foC z<5#!w4Q-sCmBo7Azb$QippE9h_vsQsJv^6!_4vLDkH0hOL)5YU zWrTU^+c;UzQBA+|uO!qSDE(`|t4m$K=d2d~SA*APycewJSebtnSZy6~wA9vv&2!G0 z-W9c|`5JIu^R@82<{RK@z7M138^Pu|XHD;$TGZSK&TGC2p4YqvuIBqQYVxm7@q6i< zHNCTHQImgtUQ+W~cwY1EaJ9`u)Vv;So^#gp-mC58*?3R36Fv{$udd@c?;zB}vlHz8 za?iVq`Tu$P;P#t)Zh)&ton2tp&pkK7)rYtZz8m_99fbAGciz7JemWPm-UGJp-9+^L zUa)$22Eq27dv1cOhv#OnednG%aP{yEf$cl@+yYmRzTXFSee0X=ynXu~an2t7eYuVB zwcCh=nmGf}_A0YIon?GD?k^ez>p8p}ZdyM=KgY~GJ?|Vuu^6v!a`Bk|4kNms9 zcM|I6R~XeIe*~Q8*Wo^!$lnjvqi%kUQO$h!H%54t&W)Bl<~{=Vc{$f$l*imS*k|!D z&uJ;+Awq462+uUw{f1`-td@J4aQ76RS+H8}nS520`47LZ?u!bJ{KEUW) z^z>11-uHcQwR z(f0%3yzkG#)sAPLhroH?pM$IAp3lSczP|uh%ROI&=Y4+(uBIpYei&>Iu3-&5_PxO9 zoZr=M#xD~qh(`(c;$3-+Q1>@N{uSa8LOt@n3O4U?BJ#clR*$@|gUwZU?Gub@;eP_G z=5LwnJViV~JW1$3$*2}i@0WV)^#^eC)T7Q1!Few~g6mVamotoN`u(o|nD9Fu`9A^MZ=C5* z!D@LNrAKprMwqWB>irz-dm-xm0_>Vm@0Vb;Jlv11vBy6UONdp3bJ6=B!R9(2 yz5fYpednV7^WcSyzW~nb{~2!mm4tIq|1V&3osar|1-piG?(ICI+JC5UJ@H?Ak- -#include - -using namespace metal; - -struct NTSCParams -{ - float source_width; - float source_height; - float a_value; - float b_value; - float cc_value; - float scan_time; - float notch_width; - float y_freq; - float i_freq; - float q_freq; - float _pad0; - float _pad1; -}; - -struct main0_out -{ - float4 FragColor [[color(0)]]; -}; - -struct main0_in -{ - float2 v_uv [[user(locn0)]]; -}; - -fragment main0_out main0(main0_in in [[stage_in]], constant NTSCParams& u [[buffer(0)]], texture2d Source [[texture(0)]], sampler SourceSmplr [[sampler(0)]]) -{ - main0_out out = {}; - float2 source_dims = float2(u.source_width, u.source_height); - float2 PValueSourceTexel = float2(1.0, 0.0) / source_dims; - float2 C0 = in.v_uv + (PValueSourceTexel * float2(0.0)); - float2 C1 = in.v_uv + (PValueSourceTexel * float2(0.25, 0.0)); - float2 C2 = in.v_uv + (PValueSourceTexel * float2(0.5, 0.0)); - float2 C3 = in.v_uv + (PValueSourceTexel * float2(0.75, 0.0)); - float4 Cx = float4(C0.x, C1.x, C2.x, C3.x); - float4 Cy = float4(C0.y, C1.y, C2.y, C3.y); - float4 Texel0 = Source.sample(SourceSmplr, C0); - float4 Texel1 = Source.sample(SourceSmplr, C1); - float4 Texel2 = Source.sample(SourceSmplr, C2); - float4 Texel3 = Source.sample(SourceSmplr, C3); - float4 HPosition = Cx; - float4 VPosition = Cy; - float4 Y = float4(dot(Texel0, float4(0.2989999949932098388671875, 0.58700001239776611328125, 0.114000000059604644775390625, 0.0)), dot(Texel1, float4(0.2989999949932098388671875, 0.58700001239776611328125, 0.114000000059604644775390625, 0.0)), dot(Texel2, float4(0.2989999949932098388671875, 0.58700001239776611328125, 0.114000000059604644775390625, 0.0)), dot(Texel3, float4(0.2989999949932098388671875, 0.58700001239776611328125, 0.114000000059604644775390625, 0.0))); - float4 I = float4(dot(Texel0, float4(0.595715999603271484375, -0.2744530141353607177734375, -0.3212629854679107666015625, 0.0)), dot(Texel1, float4(0.595715999603271484375, -0.2744530141353607177734375, -0.3212629854679107666015625, 0.0)), dot(Texel2, float4(0.595715999603271484375, -0.2744530141353607177734375, -0.3212629854679107666015625, 0.0)), dot(Texel3, float4(0.595715999603271484375, -0.2744530141353607177734375, -0.3212629854679107666015625, 0.0))); - float4 Q = float4(dot(Texel0, float4(0.211456000804901123046875, -0.52259099483489990234375, 0.311134994029998779296875, 0.0)), dot(Texel1, float4(0.211456000804901123046875, -0.52259099483489990234375, 0.311134994029998779296875, 0.0)), dot(Texel2, float4(0.211456000804901123046875, -0.52259099483489990234375, 0.311134994029998779296875, 0.0)), dot(Texel3, float4(0.211456000804901123046875, -0.52259099483489990234375, 0.311134994029998779296875, 0.0))); - float W = (6.283185482025146484375 * u.cc_value) * u.scan_time; - float WoPI = W / 3.1415927410125732421875; - float HOffset = u.a_value / WoPI; - float VScale = (u.b_value * source_dims.y) / WoPI; - float4 T = (HPosition + float4(HOffset)) + (VPosition * float4(VScale)); - float4 TW = T * W; - out.FragColor = (Y + (I * cos(TW))) + (Q * sin(TW)); - return out; -} - diff --git a/data/shaders/ntsc-md-rainbows/pass0_encode.vert b/data/shaders/ntsc-md-rainbows/pass0_encode.vert deleted file mode 100644 index 006d695..0000000 --- a/data/shaders/ntsc-md-rainbows/pass0_encode.vert +++ /dev/null @@ -1,8 +0,0 @@ -#version 450 -layout(location=0) out vec2 v_uv; -void main() { - vec2 positions[3] = vec2[3](vec2(-1.0,-1.0), vec2(3.0,-1.0), vec2(-1.0,3.0)); - vec2 uvs[3] = vec2[3](vec2(0.0, 1.0), vec2(2.0, 1.0), vec2(0.0,-1.0)); - gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); - v_uv = uvs[gl_VertexIndex]; -} diff --git a/data/shaders/ntsc-md-rainbows/pass0_encode.vert.spv b/data/shaders/ntsc-md-rainbows/pass0_encode.vert.spv deleted file mode 100644 index 4cd25bd1445ecb964d38a3c1bb5887ab73695bc2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1436 zcmYk6>rWF=5XFa<76bti0UwB4@lCC7V`5Z{nAM~S5E8y_NV_pNS(m1}ZPZWvXD0qt zelhX^a;XpAmY4SloL7iEbVi=~@z^a+ReI%?qP#dE}PoL#72dH_GF zFl+lY*0d^fv@`>C#2bp5@_}qq^?Eo3CburkO9*6)-3nh&O#PPdf;=^FfiP=>;n6eq zlFaP%z>3o;>GxL!P~JFU-$5ZppY$&h4f=dAMyw z+>x&#hjE^_49_{fQ(a5`u8clEYBqYj zFGCNHhB+R{;E$x?EFV^!+=SyhKsO^hR2BQ$m7yi)-vH~d_s6n8#u?HV7`^X{f0omI XJrkyPc;DA^#qiW~R^Wf?;En7*` -#include - -using namespace metal; - -template -struct spvUnsafeArray -{ - T elements[Num ? Num : 1]; - - thread T& operator [] (size_t pos) thread - { - return elements[pos]; - } - constexpr const thread T& operator [] (size_t pos) const thread - { - return elements[pos]; - } - - device T& operator [] (size_t pos) device - { - return elements[pos]; - } - constexpr const device T& operator [] (size_t pos) const device - { - return elements[pos]; - } - - constexpr const constant T& operator [] (size_t pos) const constant - { - return elements[pos]; - } - - threadgroup T& operator [] (size_t pos) threadgroup - { - return elements[pos]; - } - constexpr const threadgroup T& operator [] (size_t pos) const threadgroup - { - return elements[pos]; - } -}; - -constant spvUnsafeArray _18 = spvUnsafeArray({ float2(-1.0), float2(3.0, -1.0), float2(-1.0, 3.0) }); -constant spvUnsafeArray _26 = spvUnsafeArray({ float2(0.0, 1.0), float2(2.0, 1.0), float2(0.0, -1.0) }); - -struct main0_out -{ - float2 v_uv [[user(locn0)]]; - float4 gl_Position [[position]]; -}; - -vertex main0_out main0(uint gl_VertexIndex [[vertex_id]]) -{ - main0_out out = {}; - out.gl_Position = float4(_18[int(gl_VertexIndex)], 0.0, 1.0); - out.v_uv = _26[int(gl_VertexIndex)]; - return out; -} - diff --git a/data/shaders/ntsc-md-rainbows/pass1_decode.frag b/data/shaders/ntsc-md-rainbows/pass1_decode.frag deleted file mode 100644 index 444237a..0000000 --- a/data/shaders/ntsc-md-rainbows/pass1_decode.frag +++ /dev/null @@ -1,148 +0,0 @@ -#version 450 -// license:BSD-3-Clause -// copyright-holders:Ryan Holtz,ImJezze -// Adapted from mame_ntsc_decode.slang for SDL3 GPU / Vulkan SPIRV - -layout(location=0) in vec2 v_uv; -layout(location=0) out vec4 FragColor; - -layout(set=2, binding=0) uniform sampler2D Source; - -layout(set=3, binding=0) uniform NTSCParams { - float source_width; - float source_height; - float a_value; - float b_value; - float cc_value; - float scan_time; - float notch_width; - float y_freq; - float i_freq; - float q_freq; - float _pad0; - float _pad1; -} u; - -const float PI = 3.1415927; -const float PI2 = PI * 2.0; - -const vec3 RDot = vec3(1.0, 0.956, 0.621); -const vec3 GDot = vec3(1.0, -0.272, -0.647); -const vec3 BDot = vec3(1.0, -1.106, 1.703); - -const vec4 NotchOffset = vec4(0.0, 1.0, 2.0, 3.0); -const int SampleCount = 64; -const int HalfSampleCount = 32; - -void main() { - vec2 source_dims = vec2(u.source_width, u.source_height); - vec4 BaseTexel = texture(Source, v_uv); - - float CCValue = u.cc_value; - float ScanTime = u.scan_time; - float NotchHalfWidth = u.notch_width / 2.0; - float YFreqResponse = u.y_freq; - float IFreqResponse = u.i_freq; - float QFreqResponse = u.q_freq; - float AValue = u.a_value; - float BValue = u.b_value; - - float TimePerSample = ScanTime / (source_dims.x * 4.0); - - float Fc_y1 = (CCValue - NotchHalfWidth) * TimePerSample; - float Fc_y2 = (CCValue + NotchHalfWidth) * TimePerSample; - float Fc_y3 = YFreqResponse * TimePerSample; - float Fc_i = IFreqResponse * TimePerSample; - float Fc_q = QFreqResponse * TimePerSample; - - float Fc_i_2 = Fc_i * 2.0; - float Fc_q_2 = Fc_q * 2.0; - float Fc_y1_2 = Fc_y1 * 2.0; - float Fc_y2_2 = Fc_y2 * 2.0; - float Fc_y3_2 = Fc_y3 * 2.0; - float Fc_i_pi2 = Fc_i * PI2; - float Fc_q_pi2 = Fc_q * PI2; - float Fc_y1_pi2 = Fc_y1 * PI2; - float Fc_y2_pi2 = Fc_y2 * PI2; - float Fc_y3_pi2 = Fc_y3 * PI2; - float PI2Length = PI2 / float(SampleCount); - - float W = PI2 * CCValue * ScanTime; - float WoPI = W / PI; - - float HOffset = BValue / WoPI; - float VScale = AValue * source_dims.y / WoPI; - - vec4 YAccum = vec4(0.0); - vec4 IAccum = vec4(0.0); - vec4 QAccum = vec4(0.0); - - vec4 Cy = vec4(v_uv.y); - vec4 VPosition = Cy; - - for (float i = 0.0; i < float(SampleCount); i += 4.0) { - float n = i - float(HalfSampleCount); - vec4 n4 = n + NotchOffset; - - vec4 Cx = vec4(v_uv.x) + (n4 * 0.25) / source_dims.x; - vec4 HPosition = Cx; - - vec4 C = texture(Source, vec2(Cx.r, Cy.r)); - vec4 T = HPosition + vec4(HOffset) + VPosition * vec4(VScale); - vec4 WT = W * T; - - vec4 SincKernel = 0.54 + 0.46 * cos(PI2Length * n4); - - vec4 SincYIn1 = Fc_y1_pi2 * n4; - vec4 SincYIn2 = Fc_y2_pi2 * n4; - vec4 SincYIn3 = Fc_y3_pi2 * n4; - vec4 SincIIn = Fc_i_pi2 * n4; - vec4 SincQIn = Fc_q_pi2 * n4; - - vec4 SincY1, SincY2, SincY3; - SincY1.x = (SincYIn1.x != 0.0) ? sin(SincYIn1.x) / SincYIn1.x : 1.0; - SincY1.y = (SincYIn1.y != 0.0) ? sin(SincYIn1.y) / SincYIn1.y : 1.0; - SincY1.z = (SincYIn1.z != 0.0) ? sin(SincYIn1.z) / SincYIn1.z : 1.0; - SincY1.w = (SincYIn1.w != 0.0) ? sin(SincYIn1.w) / SincYIn1.w : 1.0; - SincY2.x = (SincYIn2.x != 0.0) ? sin(SincYIn2.x) / SincYIn2.x : 1.0; - SincY2.y = (SincYIn2.y != 0.0) ? sin(SincYIn2.y) / SincYIn2.y : 1.0; - SincY2.z = (SincYIn2.z != 0.0) ? sin(SincYIn2.z) / SincYIn2.z : 1.0; - SincY2.w = (SincYIn2.w != 0.0) ? sin(SincYIn2.w) / SincYIn2.w : 1.0; - SincY3.x = (SincYIn3.x != 0.0) ? sin(SincYIn3.x) / SincYIn3.x : 1.0; - SincY3.y = (SincYIn3.y != 0.0) ? sin(SincYIn3.y) / SincYIn3.y : 1.0; - SincY3.z = (SincYIn3.z != 0.0) ? sin(SincYIn3.z) / SincYIn3.z : 1.0; - SincY3.w = (SincYIn3.w != 0.0) ? sin(SincYIn3.w) / SincYIn3.w : 1.0; - - vec4 IdealY = Fc_y1_2 * SincY1 - Fc_y2_2 * SincY2 + Fc_y3_2 * SincY3; - - vec4 IdealI, IdealQ; - IdealI.x = Fc_i_2 * ((SincIIn.x != 0.0) ? sin(SincIIn.x) / SincIIn.x : 1.0); - IdealI.y = Fc_i_2 * ((SincIIn.y != 0.0) ? sin(SincIIn.y) / SincIIn.y : 1.0); - IdealI.z = Fc_i_2 * ((SincIIn.z != 0.0) ? sin(SincIIn.z) / SincIIn.z : 1.0); - IdealI.w = Fc_i_2 * ((SincIIn.w != 0.0) ? sin(SincIIn.w) / SincIIn.w : 1.0); - IdealQ.x = Fc_q_2 * ((SincQIn.x != 0.0) ? sin(SincQIn.x) / SincQIn.x : 1.0); - IdealQ.y = Fc_q_2 * ((SincQIn.y != 0.0) ? sin(SincQIn.y) / SincQIn.y : 1.0); - IdealQ.z = Fc_q_2 * ((SincQIn.z != 0.0) ? sin(SincQIn.z) / SincQIn.z : 1.0); - IdealQ.w = Fc_q_2 * ((SincQIn.w != 0.0) ? sin(SincQIn.w) / SincQIn.w : 1.0); - - vec4 FilterY = SincKernel * IdealY; - vec4 FilterI = SincKernel * IdealI; - vec4 FilterQ = SincKernel * IdealQ; - - YAccum += C * FilterY; - IAccum += C * cos(WT) * FilterI; - QAccum += C * sin(WT) * FilterQ; - } - - vec3 YIQ = vec3( - (YAccum.r + YAccum.g + YAccum.b + YAccum.a), - (IAccum.r + IAccum.g + IAccum.b + IAccum.a) * 2.0, - (QAccum.r + QAccum.g + QAccum.b + QAccum.a) * 2.0); - - vec3 RGB = vec3( - dot(YIQ, RDot), - dot(YIQ, GDot), - dot(YIQ, BDot)); - - FragColor = vec4(RGB, BaseTexel.a); -} diff --git a/data/shaders/ntsc-md-rainbows/pass1_decode.frag.spv b/data/shaders/ntsc-md-rainbows/pass1_decode.frag.spv deleted file mode 100644 index 5fc1317938b9f0880bf97e22f6230fe278fb33e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16192 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 diff --git a/data/shaders/ntsc-md-rainbows/pass1_decode.frag.spv.msl b/data/shaders/ntsc-md-rainbows/pass1_decode.frag.spv.msl deleted file mode 100644 index 7f8c003..0000000 --- a/data/shaders/ntsc-md-rainbows/pass1_decode.frag.spv.msl +++ /dev/null @@ -1,304 +0,0 @@ -#include -#include - -using namespace metal; - -struct NTSCParams -{ - float source_width; - float source_height; - float a_value; - float b_value; - float cc_value; - float scan_time; - float notch_width; - float y_freq; - float i_freq; - float q_freq; - float _pad0; - float _pad1; -}; - -struct main0_out -{ - float4 FragColor [[color(0)]]; -}; - -struct main0_in -{ - float2 v_uv [[user(locn0)]]; -}; - -fragment main0_out main0(main0_in in [[stage_in]], constant NTSCParams& u [[buffer(0)]], texture2d Source [[texture(0)]], sampler SourceSmplr [[sampler(0)]]) -{ - main0_out out = {}; - float2 source_dims = float2(u.source_width, u.source_height); - float4 BaseTexel = Source.sample(SourceSmplr, in.v_uv); - float CCValue = u.cc_value; - float ScanTime = u.scan_time; - float NotchHalfWidth = u.notch_width / 2.0; - float YFreqResponse = u.y_freq; - float IFreqResponse = u.i_freq; - float QFreqResponse = u.q_freq; - float AValue = u.a_value; - float BValue = u.b_value; - float TimePerSample = ScanTime / (source_dims.x * 4.0); - float Fc_y1 = (CCValue - NotchHalfWidth) * TimePerSample; - float Fc_y2 = (CCValue + NotchHalfWidth) * TimePerSample; - float Fc_y3 = YFreqResponse * TimePerSample; - float Fc_i = IFreqResponse * TimePerSample; - float Fc_q = QFreqResponse * TimePerSample; - float Fc_i_2 = Fc_i * 2.0; - float Fc_q_2 = Fc_q * 2.0; - float Fc_y1_2 = Fc_y1 * 2.0; - float Fc_y2_2 = Fc_y2 * 2.0; - float Fc_y3_2 = Fc_y3 * 2.0; - float Fc_i_pi2 = Fc_i * 6.283185482025146484375; - float Fc_q_pi2 = Fc_q * 6.283185482025146484375; - float Fc_y1_pi2 = Fc_y1 * 6.283185482025146484375; - float Fc_y2_pi2 = Fc_y2 * 6.283185482025146484375; - float Fc_y3_pi2 = Fc_y3 * 6.283185482025146484375; - float PI2Length = 0.098174773156642913818359375; - float W = (6.283185482025146484375 * CCValue) * ScanTime; - float WoPI = W / 3.1415927410125732421875; - float HOffset = BValue / WoPI; - float VScale = (AValue * source_dims.y) / WoPI; - float4 YAccum = float4(0.0); - float4 IAccum = float4(0.0); - float4 QAccum = float4(0.0); - float4 Cy = float4(in.v_uv.y); - float4 VPosition = Cy; - float4 SincY1; - float _259; - float _274; - float _290; - float _306; - float4 SincY2; - float _322; - float _337; - float _352; - float _367; - float4 SincY3; - float _383; - float _398; - float _413; - float _428; - float4 IdealI; - float _457; - float _474; - float _491; - float _508; - float4 IdealQ; - float _526; - float _543; - float _560; - float _577; - for (float i = 0.0; i < 64.0; i += 4.0) - { - float n = i - 32.0; - float4 n4 = float4(n) + float4(0.0, 1.0, 2.0, 3.0); - float4 Cx = float4(in.v_uv.x) + ((n4 * 0.25) / float4(source_dims.x)); - float4 HPosition = Cx; - float4 C = Source.sample(SourceSmplr, float2(Cx.x, Cy.x)); - float4 T = (HPosition + float4(HOffset)) + (VPosition * float4(VScale)); - float4 WT = T * W; - float4 SincKernel = float4(0.540000021457672119140625) + (cos(n4 * PI2Length) * 0.4600000083446502685546875); - float4 SincYIn1 = n4 * Fc_y1_pi2; - float4 SincYIn2 = n4 * Fc_y2_pi2; - float4 SincYIn3 = n4 * Fc_y3_pi2; - float4 SincIIn = n4 * Fc_i_pi2; - float4 SincQIn = n4 * Fc_q_pi2; - if (SincYIn1.x != 0.0) - { - _259 = sin(SincYIn1.x) / SincYIn1.x; - } - else - { - _259 = 1.0; - } - SincY1.x = _259; - if (SincYIn1.y != 0.0) - { - _274 = sin(SincYIn1.y) / SincYIn1.y; - } - else - { - _274 = 1.0; - } - SincY1.y = _274; - if (SincYIn1.z != 0.0) - { - _290 = sin(SincYIn1.z) / SincYIn1.z; - } - else - { - _290 = 1.0; - } - SincY1.z = _290; - if (SincYIn1.w != 0.0) - { - _306 = sin(SincYIn1.w) / SincYIn1.w; - } - else - { - _306 = 1.0; - } - SincY1.w = _306; - if (SincYIn2.x != 0.0) - { - _322 = sin(SincYIn2.x) / SincYIn2.x; - } - else - { - _322 = 1.0; - } - SincY2.x = _322; - if (SincYIn2.y != 0.0) - { - _337 = sin(SincYIn2.y) / SincYIn2.y; - } - else - { - _337 = 1.0; - } - SincY2.y = _337; - if (SincYIn2.z != 0.0) - { - _352 = sin(SincYIn2.z) / SincYIn2.z; - } - else - { - _352 = 1.0; - } - SincY2.z = _352; - if (SincYIn2.w != 0.0) - { - _367 = sin(SincYIn2.w) / SincYIn2.w; - } - else - { - _367 = 1.0; - } - SincY2.w = _367; - if (SincYIn3.x != 0.0) - { - _383 = sin(SincYIn3.x) / SincYIn3.x; - } - else - { - _383 = 1.0; - } - SincY3.x = _383; - if (SincYIn3.y != 0.0) - { - _398 = sin(SincYIn3.y) / SincYIn3.y; - } - else - { - _398 = 1.0; - } - SincY3.y = _398; - if (SincYIn3.z != 0.0) - { - _413 = sin(SincYIn3.z) / SincYIn3.z; - } - else - { - _413 = 1.0; - } - SincY3.z = _413; - if (SincYIn3.w != 0.0) - { - _428 = sin(SincYIn3.w) / SincYIn3.w; - } - else - { - _428 = 1.0; - } - SincY3.w = _428; - float4 IdealY = ((SincY1 * Fc_y1_2) - (SincY2 * Fc_y2_2)) + (SincY3 * Fc_y3_2); - if (SincIIn.x != 0.0) - { - _457 = sin(SincIIn.x) / SincIIn.x; - } - else - { - _457 = 1.0; - } - IdealI.x = Fc_i_2 * _457; - if (SincIIn.y != 0.0) - { - _474 = sin(SincIIn.y) / SincIIn.y; - } - else - { - _474 = 1.0; - } - IdealI.y = Fc_i_2 * _474; - if (SincIIn.z != 0.0) - { - _491 = sin(SincIIn.z) / SincIIn.z; - } - else - { - _491 = 1.0; - } - IdealI.z = Fc_i_2 * _491; - if (SincIIn.w != 0.0) - { - _508 = sin(SincIIn.w) / SincIIn.w; - } - else - { - _508 = 1.0; - } - IdealI.w = Fc_i_2 * _508; - if (SincQIn.x != 0.0) - { - _526 = sin(SincQIn.x) / SincQIn.x; - } - else - { - _526 = 1.0; - } - IdealQ.x = Fc_q_2 * _526; - if (SincQIn.y != 0.0) - { - _543 = sin(SincQIn.y) / SincQIn.y; - } - else - { - _543 = 1.0; - } - IdealQ.y = Fc_q_2 * _543; - if (SincQIn.z != 0.0) - { - _560 = sin(SincQIn.z) / SincQIn.z; - } - else - { - _560 = 1.0; - } - IdealQ.z = Fc_q_2 * _560; - if (SincQIn.w != 0.0) - { - _577 = sin(SincQIn.w) / SincQIn.w; - } - else - { - _577 = 1.0; - } - IdealQ.w = Fc_q_2 * _577; - float4 FilterY = SincKernel * IdealY; - float4 FilterI = SincKernel * IdealI; - float4 FilterQ = SincKernel * IdealQ; - YAccum += (C * FilterY); - IAccum += ((C * cos(WT)) * FilterI); - QAccum += ((C * sin(WT)) * FilterQ); - } - float3 YIQ = float3(((YAccum.x + YAccum.y) + YAccum.z) + YAccum.w, (((IAccum.x + IAccum.y) + IAccum.z) + IAccum.w) * 2.0, (((QAccum.x + QAccum.y) + QAccum.z) + QAccum.w) * 2.0); - float3 RGB = float3(dot(YIQ, float3(1.0, 0.95599997043609619140625, 0.620999991893768310546875)), dot(YIQ, float3(1.0, -0.272000014781951904296875, -0.647000014781951904296875)), dot(YIQ, float3(1.0, -1.10599994659423828125, 1.70299994945526123046875))); - out.FragColor = float4(RGB, BaseTexel.w); - return out; -} - diff --git a/data/shaders/ntsc-md-rainbows/pass1_decode.vert b/data/shaders/ntsc-md-rainbows/pass1_decode.vert deleted file mode 100644 index 006d695..0000000 --- a/data/shaders/ntsc-md-rainbows/pass1_decode.vert +++ /dev/null @@ -1,8 +0,0 @@ -#version 450 -layout(location=0) out vec2 v_uv; -void main() { - vec2 positions[3] = vec2[3](vec2(-1.0,-1.0), vec2(3.0,-1.0), vec2(-1.0,3.0)); - vec2 uvs[3] = vec2[3](vec2(0.0, 1.0), vec2(2.0, 1.0), vec2(0.0,-1.0)); - gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); - v_uv = uvs[gl_VertexIndex]; -} diff --git a/data/shaders/ntsc-md-rainbows/pass1_decode.vert.spv b/data/shaders/ntsc-md-rainbows/pass1_decode.vert.spv deleted file mode 100644 index 4cd25bd1445ecb964d38a3c1bb5887ab73695bc2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1436 zcmYk6>rWF=5XFa<76bti0UwB4@lCC7V`5Z{nAM~S5E8y_NV_pNS(m1}ZPZWvXD0qt zelhX^a;XpAmY4SloL7iEbVi=~@z^a+ReI%?qP#dE}PoL#72dH_GF zFl+lY*0d^fv@`>C#2bp5@_}qq^?Eo3CburkO9*6)-3nh&O#PPdf;=^FfiP=>;n6eq zlFaP%z>3o;>GxL!P~JFU-$5ZppY$&h4f=dAMyw z+>x&#hjE^_49_{fQ(a5`u8clEYBqYj zFGCNHhB+R{;E$x?EFV^!+=SyhKsO^hR2BQ$m7yi)-vH~d_s6n8#u?HV7`^X{f0omI XJrkyPc;DA^#qiW~R^Wf?;En7*` -#include - -using namespace metal; - -template -struct spvUnsafeArray -{ - T elements[Num ? Num : 1]; - - thread T& operator [] (size_t pos) thread - { - return elements[pos]; - } - constexpr const thread T& operator [] (size_t pos) const thread - { - return elements[pos]; - } - - device T& operator [] (size_t pos) device - { - return elements[pos]; - } - constexpr const device T& operator [] (size_t pos) const device - { - return elements[pos]; - } - - constexpr const constant T& operator [] (size_t pos) const constant - { - return elements[pos]; - } - - threadgroup T& operator [] (size_t pos) threadgroup - { - return elements[pos]; - } - constexpr const threadgroup T& operator [] (size_t pos) const threadgroup - { - return elements[pos]; - } -}; - -constant spvUnsafeArray _18 = spvUnsafeArray({ float2(-1.0), float2(3.0, -1.0), float2(-1.0, 3.0) }); -constant spvUnsafeArray _26 = spvUnsafeArray({ float2(0.0, 1.0), float2(2.0, 1.0), float2(0.0, -1.0) }); - -struct main0_out -{ - float2 v_uv [[user(locn0)]]; - float4 gl_Position [[position]]; -}; - -vertex main0_out main0(uint gl_VertexIndex [[vertex_id]]) -{ - main0_out out = {}; - out.gl_Position = float4(_18[int(gl_VertexIndex)], 0.0, 1.0); - out.v_uv = _26[int(gl_VertexIndex)]; - return out; -} - diff --git a/data/shaders/ntsc-md-rainbows/preset.ini b/data/shaders/ntsc-md-rainbows/preset.ini deleted file mode 100644 index 6a9ac83..0000000 --- a/data/shaders/ntsc-md-rainbows/preset.ini +++ /dev/null @@ -1,18 +0,0 @@ -name = ntsc-md-rainbows -passes = 2 - -pass0_vert = pass0_encode.vert -pass0_frag = pass0_encode.frag - -pass1_vert = pass1_decode.vert -pass1_frag = pass1_decode.frag - -; NTSC parameters (mapped to NTSCParams uniform buffer) -scantime = 47.9 -avalue = 0.0 -bvalue = 0.0 -ccvalue = 3.5795455 -notch_width = 3.45 -yfreqresponse = 1.75 -ifreqresponse = 1.75 -qfreqresponse = 1.45 diff --git a/source/engine.cpp b/source/engine.cpp index e4da1c4..b3222ea 100644 --- a/source/engine.cpp +++ b/source/engine.cpp @@ -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(); - 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(current_screen_width_); - ntsc.source_height = static_cast(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(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_); } diff --git a/source/engine.hpp b/source/engine.hpp index 035ec9e..f2822a2 100644 --- a/source/engine.hpp +++ b/source/engine.hpp @@ -19,10 +19,8 @@ #include "gpu/gpu_ball_buffer.hpp" // for GpuBallBuffer, BallGPUData #include "gpu/gpu_context.hpp" // for GpuContext #include "gpu/gpu_pipeline.hpp" // for GpuPipeline -#include "gpu/gpu_shader_preset.hpp" // for NTSCParams, GpuShaderPreset #include "gpu/gpu_sprite_batch.hpp" // for GpuSpriteBatch #include "gpu/gpu_texture.hpp" // for GpuTexture -#include "gpu/shader_manager.hpp" // for ShaderManager #include "input/input_handler.hpp" // for InputHandler #include "scene/scene_manager.hpp" // for SceneManager #include "shapes_mgr/shape_manager.hpp" // for ShapeManager @@ -84,10 +82,8 @@ class Engine { void setInitialPostFX(int mode); void setPostFXParamOverrides(float vignette, float chroma); - // External shader presets (loaded from data/shaders/) + // Cicle PostFX nadiu (OFF → Vinyeta → Scanlines → Cromàtica → Complet) void cycleShader(); - void setInitialShader(const std::string& name); - std::string getActiveShaderName() const; // Modo kiosko void setKioskMode(bool enabled) { kiosk_mode_ = enabled; } @@ -137,7 +133,6 @@ class Engine { float getPostFXVignette() const { return postfx_uniforms_.vignette_strength; } float getPostFXChroma() const { return postfx_uniforms_.chroma_strength; } float getPostFXScanline() const { return postfx_uniforms_.scanline_strength; } - bool isExternalShaderActive() const { return active_shader_ != nullptr; } private: // === Componentes del sistema (Composición) === @@ -192,11 +187,8 @@ class Engine { float postfx_override_vignette_ = -1.f; // -1 = sin override float postfx_override_chroma_ = -1.f; - // External shader system - std::unique_ptr shader_manager_; - GpuShaderPreset* active_shader_ = nullptr; // null = native PostFX - int active_shader_idx_ = -1; // index into shader_manager_->names() - std::string initial_shader_name_; // set before initialize() + // Cicle PostFX natiu (0=OFF, 1=Vinyeta, 2=Scanlines, 3=Cromàtica, 4=Complet) + int postfx_cycle_idx_ = 0; // Sistema de zoom dinámico int current_window_zoom_ = DEFAULT_WINDOW_ZOOM; diff --git a/source/gpu/gpu_shader_preset.cpp b/source/gpu/gpu_shader_preset.cpp deleted file mode 100644 index bfb5222..0000000 --- a/source/gpu/gpu_shader_preset.cpp +++ /dev/null @@ -1,274 +0,0 @@ -#include "gpu_shader_preset.hpp" -#include "gpu_texture.hpp" - -#include -#include -#include -#include -#include - -// ============================================================================ -// Helpers -// ============================================================================ - -static std::vector readFile(const std::string& path) { - std::ifstream f(path, std::ios::binary | std::ios::ate); - if (!f) return {}; - std::streamsize sz = f.tellg(); - f.seekg(0, std::ios::beg); - std::vector buf(static_cast(sz)); - if (!f.read(reinterpret_cast(buf.data()), sz)) return {}; - return buf; -} - -static std::string trim(const std::string& s) { - size_t a = s.find_first_not_of(" \t\r\n"); - if (a == std::string::npos) return {}; - size_t b = s.find_last_not_of(" \t\r\n"); - return s.substr(a, b - a + 1); -} - -// ============================================================================ -// GpuShaderPreset -// ============================================================================ - -bool GpuShaderPreset::parseIni(const std::string& ini_path) { - std::ifstream f(ini_path); - if (!f) { - SDL_Log("GpuShaderPreset: cannot open %s", ini_path.c_str()); - return false; - } - - int num_passes = 0; - std::string line; - while (std::getline(f, line)) { - // Strip comments - auto comment = line.find(';'); - if (comment != std::string::npos) line = line.substr(0, comment); - line = trim(line); - if (line.empty()) continue; - - auto eq = line.find('='); - if (eq == std::string::npos) continue; - - std::string key = trim(line.substr(0, eq)); - std::string value = trim(line.substr(eq + 1)); - if (key.empty() || value.empty()) continue; - - if (key == "name") { - name_ = value; - } else if (key == "passes") { - num_passes = std::stoi(value); - } else { - // Try to parse as float parameter - try { - params_[key] = std::stof(value); - } catch (...) { - // Non-float values stored separately (pass0_vert etc.) - } - } - } - - if (num_passes <= 0) { - SDL_Log("GpuShaderPreset: no passes defined in %s", ini_path.c_str()); - return false; - } - - // Second pass: read per-pass file names - f.clear(); - f.seekg(0, std::ios::beg); - descs_.resize(num_passes); - while (std::getline(f, line)) { - auto comment = line.find(';'); - if (comment != std::string::npos) line = line.substr(0, comment); - line = trim(line); - if (line.empty()) continue; - auto eq = line.find('='); - if (eq == std::string::npos) continue; - std::string key = trim(line.substr(0, eq)); - std::string value = trim(line.substr(eq + 1)); - - for (int i = 0; i < num_passes; ++i) { - std::string vi = "pass" + std::to_string(i) + "_vert"; - std::string fi = "pass" + std::to_string(i) + "_frag"; - if (key == vi) descs_[i].vert_name = value; - if (key == fi) descs_[i].frag_name = value; - } - } - - // Validate - for (int i = 0; i < num_passes; ++i) { - if (descs_[i].vert_name.empty() || descs_[i].frag_name.empty()) { - SDL_Log("GpuShaderPreset: pass %d missing vert or frag in %s", i, ini_path.c_str()); - return false; - } - } - return true; -} - -SDL_GPUGraphicsPipeline* GpuShaderPreset::buildPassPipeline(SDL_GPUDevice* device, - const std::string& vert_spv_path, - const std::string& frag_spv_path, - SDL_GPUTextureFormat target_fmt) { -#ifdef __APPLE__ - constexpr SDL_GPUShaderFormat kFmt = SDL_GPU_SHADERFORMAT_MSL; - constexpr const char* kEntry = "main0"; -#else - constexpr SDL_GPUShaderFormat kFmt = SDL_GPU_SHADERFORMAT_SPIRV; - constexpr const char* kEntry = "main"; -#endif - - auto vert_spv = readFile(vert_spv_path); - auto frag_spv = readFile(frag_spv_path); - if (vert_spv.empty()) { - SDL_Log("GpuShaderPreset: cannot read %s", vert_spv_path.c_str()); - return nullptr; - } - if (frag_spv.empty()) { - SDL_Log("GpuShaderPreset: cannot read %s", frag_spv_path.c_str()); - return nullptr; - } -#ifdef __APPLE__ - vert_spv.push_back(0); - frag_spv.push_back(0); -#endif - - SDL_GPUShaderCreateInfo vert_info = {}; - vert_info.code = vert_spv.data(); - vert_info.code_size = vert_spv.size(); - vert_info.entrypoint = kEntry; - vert_info.format = kFmt; - vert_info.stage = SDL_GPU_SHADERSTAGE_VERTEX; - vert_info.num_samplers = 0; - vert_info.num_uniform_buffers = 0; - - SDL_GPUShaderCreateInfo frag_info = {}; - frag_info.code = frag_spv.data(); - frag_info.code_size = frag_spv.size(); - frag_info.entrypoint = kEntry; - frag_info.format = kFmt; - frag_info.stage = SDL_GPU_SHADERSTAGE_FRAGMENT; - frag_info.num_samplers = 1; - frag_info.num_uniform_buffers = 1; - - SDL_GPUShader* vert_shader = SDL_CreateGPUShader(device, &vert_info); - SDL_GPUShader* frag_shader = SDL_CreateGPUShader(device, &frag_info); - - if (!vert_shader || !frag_shader) { - SDL_Log("GpuShaderPreset: shader creation failed for %s / %s: %s", - vert_spv_path.c_str(), frag_spv_path.c_str(), SDL_GetError()); - if (vert_shader) SDL_ReleaseGPUShader(device, vert_shader); - if (frag_shader) SDL_ReleaseGPUShader(device, frag_shader); - return nullptr; - } - - // Full-screen triangle: no vertex input, no blend - SDL_GPUColorTargetBlendState no_blend = {}; - no_blend.enable_blend = false; - no_blend.enable_color_write_mask = false; - - SDL_GPUColorTargetDescription ct_desc = {}; - ct_desc.format = target_fmt; - ct_desc.blend_state = no_blend; - - SDL_GPUVertexInputState no_input = {}; - - SDL_GPUGraphicsPipelineCreateInfo pipe_info = {}; - pipe_info.vertex_shader = vert_shader; - pipe_info.fragment_shader = frag_shader; - pipe_info.vertex_input_state = no_input; - pipe_info.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST; - pipe_info.target_info.num_color_targets = 1; - pipe_info.target_info.color_target_descriptions = &ct_desc; - - SDL_GPUGraphicsPipeline* pipeline = SDL_CreateGPUGraphicsPipeline(device, &pipe_info); - - SDL_ReleaseGPUShader(device, vert_shader); - SDL_ReleaseGPUShader(device, frag_shader); - - if (!pipeline) - SDL_Log("GpuShaderPreset: pipeline creation failed: %s", SDL_GetError()); - - return pipeline; -} - -bool GpuShaderPreset::load(SDL_GPUDevice* device, - const std::string& dir, - SDL_GPUTextureFormat swapchain_fmt, - int w, int h) { - dir_ = dir; - swapchain_fmt_ = swapchain_fmt; - - // Parse ini - if (!parseIni(dir + "/preset.ini")) - return false; - - int n = static_cast(descs_.size()); - passes_.resize(n); - - // Intermediate render target format (signed float to handle NTSC signal range) - SDL_GPUTextureFormat inter_fmt = SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT; - - for (int i = 0; i < n; ++i) { - bool is_last = (i == n - 1); - SDL_GPUTextureFormat target_fmt = is_last ? swapchain_fmt : inter_fmt; - -#ifdef __APPLE__ - const char* ext = ".spv.msl"; -#else - const char* ext = ".spv"; -#endif - std::string vert_spv = dir + "/" + descs_[i].vert_name + ext; - std::string frag_spv = dir + "/" + descs_[i].frag_name + ext; - - passes_[i].pipeline = buildPassPipeline(device, vert_spv, frag_spv, target_fmt); - if (!passes_[i].pipeline) { - SDL_Log("GpuShaderPreset: failed to build pipeline for pass %d", i); - return false; - } - - if (!is_last) { - // Create intermediate render target - auto tex = std::make_unique(); - if (!tex->createRenderTarget(device, w, h, inter_fmt)) { - SDL_Log("GpuShaderPreset: failed to create intermediate target for pass %d", i); - return false; - } - passes_[i].target = tex.get(); - targets_.push_back(std::move(tex)); - } - // Last pass: target = null (caller binds swapchain) - } - - SDL_Log("GpuShaderPreset: loaded '%s' (%d passes)", name_.c_str(), n); - return true; -} - -void GpuShaderPreset::recreateTargets(SDL_GPUDevice* device, int w, int h) { - SDL_GPUTextureFormat inter_fmt = SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT; - for (auto& tex : targets_) { - tex->destroy(device); - tex->createRenderTarget(device, w, h, inter_fmt); - } -} - -void GpuShaderPreset::destroy(SDL_GPUDevice* device) { - for (auto& pass : passes_) { - if (pass.pipeline) { - SDL_ReleaseGPUGraphicsPipeline(device, pass.pipeline); - pass.pipeline = nullptr; - } - } - for (auto& tex : targets_) { - if (tex) tex->destroy(device); - } - targets_.clear(); - passes_.clear(); - descs_.clear(); - params_.clear(); -} - -float GpuShaderPreset::param(const std::string& key, float default_val) const { - auto it = params_.find(key); - return (it != params_.end()) ? it->second : default_val; -} diff --git a/source/gpu/gpu_shader_preset.hpp b/source/gpu/gpu_shader_preset.hpp deleted file mode 100644 index a41595b..0000000 --- a/source/gpu/gpu_shader_preset.hpp +++ /dev/null @@ -1,94 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "gpu_texture.hpp" - -// ============================================================================ -// NTSCParams — uniform buffer for NTSC shader passes (set=3, binding=0) -// Matches the layout in pass0_encode.frag and pass1_decode.frag. -// Pushed via SDL_PushGPUFragmentUniformData(cmd, 0, &ntsc, sizeof(NTSCParams)). -// ============================================================================ -struct NTSCParams { - float source_width; - float source_height; - float a_value; - float b_value; - float cc_value; - float scan_time; - float notch_width; - float y_freq; - float i_freq; - float q_freq; - float _pad[2]; -}; -static_assert(sizeof(NTSCParams) == 48, "NTSCParams must be 48 bytes"); - -// ============================================================================ -// ShaderPass — one render pass in a multi-pass shader preset -// ============================================================================ -struct ShaderPass { - SDL_GPUGraphicsPipeline* pipeline = nullptr; - GpuTexture* target = nullptr; // null = swapchain (last pass) -}; - -// ============================================================================ -// GpuShaderPreset — loads and owns a multi-pass shader preset from disk. -// -// Directory layout: -// /preset.ini — descriptor -// /pass0_xxx.vert — GLSL 4.50 vertex shader source -// /pass0_xxx.frag — GLSL 4.50 fragment shader source -// /pass0_xxx.vert.spv — compiled SPIRV (by CMake/glslc at build time) -// /pass0_xxx.frag.spv — compiled SPIRV -// ... -// ============================================================================ -class GpuShaderPreset { -public: - // Load preset from directory. swapchain_fmt is the target format for the - // last pass; intermediate passes use R16G16B16A16_FLOAT. - bool load(SDL_GPUDevice* device, - const std::string& dir, - SDL_GPUTextureFormat swapchain_fmt, - int w, int h); - - void destroy(SDL_GPUDevice* device); - - // Recreate intermediate render targets on resolution change. - void recreateTargets(SDL_GPUDevice* device, int w, int h); - - int passCount() const { return static_cast(passes_.size()); } - ShaderPass& pass(int i) { return passes_[i]; } - - const std::string& name() const { return name_; } - - // Read a float parameter parsed from preset.ini (returns default_val if absent). - float param(const std::string& key, float default_val) const; - -private: - std::vector passes_; - std::vector> targets_; // intermediate render targets - std::string name_; - std::string dir_; - std::unordered_map params_; - SDL_GPUTextureFormat swapchain_fmt_ = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM; - - // Entries read from preset.ini for each pass - struct PassDesc { - std::string vert_name; // e.g. "pass0_encode.vert" - std::string frag_name; // e.g. "pass0_encode.frag" - }; - std::vector descs_; - - bool parseIni(const std::string& ini_path); - - // Build a full-screen-triangle pipeline from two on-disk SPV files. - SDL_GPUGraphicsPipeline* buildPassPipeline(SDL_GPUDevice* device, - const std::string& vert_spv_path, - const std::string& frag_spv_path, - SDL_GPUTextureFormat target_fmt); -}; diff --git a/source/gpu/shader_manager.cpp b/source/gpu/shader_manager.cpp deleted file mode 100644 index dc4a5ff..0000000 --- a/source/gpu/shader_manager.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include "shader_manager.hpp" - -#include -#include -#include - -namespace fs = std::filesystem; - -void ShaderManager::scan(const std::string& root_dir) { - root_dir_ = root_dir; - names_.clear(); - dirs_.clear(); - - std::error_code ec; - for (const auto& entry : fs::directory_iterator(root_dir, ec)) { - if (!entry.is_directory()) continue; - fs::path ini = entry.path() / "preset.ini"; - if (!fs::exists(ini)) continue; - - std::string preset_name = entry.path().filename().string(); - names_.push_back(preset_name); - dirs_[preset_name] = entry.path().string(); - } - - if (ec) { - SDL_Log("ShaderManager: scan error on %s: %s", root_dir.c_str(), ec.message().c_str()); - } - - std::sort(names_.begin(), names_.end()); - SDL_Log("ShaderManager: found %d preset(s) in %s", (int)names_.size(), root_dir.c_str()); -} - -GpuShaderPreset* ShaderManager::load(SDL_GPUDevice* device, - const std::string& name, - SDL_GPUTextureFormat swapchain_fmt, - int w, int h) { - auto it = loaded_.find(name); - if (it != loaded_.end()) return it->second.get(); - - auto dir_it = dirs_.find(name); - if (dir_it == dirs_.end()) { - SDL_Log("ShaderManager: preset '%s' not found", name.c_str()); - return nullptr; - } - - auto preset = std::make_unique(); - if (!preset->load(device, dir_it->second, swapchain_fmt, w, h)) { - SDL_Log("ShaderManager: failed to load preset '%s'", name.c_str()); - return nullptr; - } - - GpuShaderPreset* raw = preset.get(); - loaded_[name] = std::move(preset); - return raw; -} - -void ShaderManager::onResize(SDL_GPUDevice* device, SDL_GPUTextureFormat /*swapchain_fmt*/, int w, int h) { - for (auto& [name, preset] : loaded_) { - preset->recreateTargets(device, w, h); - } -} - -void ShaderManager::destroyAll(SDL_GPUDevice* device) { - for (auto& [name, preset] : loaded_) { - preset->destroy(device); - } - loaded_.clear(); -} diff --git a/source/gpu/shader_manager.hpp b/source/gpu/shader_manager.hpp deleted file mode 100644 index b0ae631..0000000 --- a/source/gpu/shader_manager.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "gpu_shader_preset.hpp" - -// ============================================================================ -// ShaderManager — discovers and manages runtime shader presets under -// a root directory (e.g., data/shaders/). -// -// Each subdirectory with a preset.ini is treated as a shader preset. -// ============================================================================ -class ShaderManager { -public: - // Scan root_dir for preset subdirectories (each must contain preset.ini). - void scan(const std::string& root_dir); - - // Available preset names (e.g. {"ntsc-md-rainbows"}). - const std::vector& names() const { return names_; } - - // Load and return a preset (cached). Returns null on failure. - GpuShaderPreset* load(SDL_GPUDevice* device, - const std::string& name, - SDL_GPUTextureFormat swapchain_fmt, - int w, int h); - - // Recreate intermediate render targets on resolution change. - void onResize(SDL_GPUDevice* device, SDL_GPUTextureFormat swapchain_fmt, int w, int h); - - void destroyAll(SDL_GPUDevice* device); - -private: - std::string root_dir_; - std::vector names_; - std::map dirs_; - std::map> loaded_; -}; diff --git a/source/main.cpp b/source/main.cpp index 07b0804..af4c125 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -24,7 +24,6 @@ void printHelp() { std::cout << " --postfx [efecto] Arrancar con PostFX activo (default: complet): vinyeta, scanlines, cromatica, complet\n"; std::cout << " --vignette Sobreescribir vignette_strength (activa PostFX si no hay --postfx)\n"; std::cout << " --chroma Sobreescribir chroma_strength (activa PostFX si no hay --postfx)\n"; - std::cout << " --shader Arrancar con shader externo (ej: ntsc-md-rainbows)\n"; std::cout << " --help Mostrar esta ayuda\n\n"; std::cout << "Ejemplos:\n"; std::cout << " vibe3_physics # 320x240 zoom 3 (ventana 960x720)\n"; @@ -52,7 +51,6 @@ int main(int argc, char* argv[]) { int initial_postfx = -1; float override_vignette = -1.f; float override_chroma = -1.f; - std::string initial_shader; AppMode initial_mode = AppMode::SANDBOX; // Modo inicial (default: SANDBOX) // Parsear argumentos @@ -177,13 +175,6 @@ int main(int argc, char* argv[]) { std::cerr << "Error: --max-balls requiere un valor\n"; return -1; } - } else if (strcmp(argv[i], "--shader") == 0) { - if (i + 1 < argc) { - initial_shader = argv[++i]; - } else { - std::cerr << "Error: --shader requiere un nombre de preset\n"; - return -1; - } } else { std::cerr << "Error: Opción desconocida '" << argv[i] << "'\n"; printHelp(); @@ -215,9 +206,6 @@ int main(int argc, char* argv[]) { engine.setPostFXParamOverrides(override_vignette, override_chroma); } - if (!initial_shader.empty()) - engine.setInitialShader(initial_shader); - if (!engine.initialize(width, height, zoom, fullscreen, initial_mode)) { std::cout << "¡Error al inicializar el engine!" << std::endl; return -1; diff --git a/source/ui/ui_manager.cpp b/source/ui/ui_manager.cpp index f4873a3..de5ba13 100644 --- a/source/ui/ui_manager.cpp +++ b/source/ui/ui_manager.cpp @@ -336,9 +336,7 @@ void UIManager::renderDebugHUD(const Engine* engine, lines.push_back(refresh_text); lines.push_back(theme_text); std::string postfx_text; - if (engine->isExternalShaderActive()) { - postfx_text = "Shader: " + engine->getActiveShaderName(); - } else if (!engine->isPostFXEnabled()) { + if (!engine->isPostFXEnabled()) { postfx_text = "PostFX: OFF"; } else { static constexpr const char* preset_names[4] = {