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 7cfa552..0000000 Binary files a/data/shaders/ntsc-md-rainbows/pass0_encode.frag.spv and /dev/null differ diff --git a/data/shaders/ntsc-md-rainbows/pass0_encode.frag.spv.msl b/data/shaders/ntsc-md-rainbows/pass0_encode.frag.spv.msl deleted file mode 100644 index ea468df..0000000 --- a/data/shaders/ntsc-md-rainbows/pass0_encode.frag.spv.msl +++ /dev/null @@ -1,61 +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); - 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 4cd25bd..0000000 Binary files a/data/shaders/ntsc-md-rainbows/pass0_encode.vert.spv and /dev/null differ diff --git a/data/shaders/ntsc-md-rainbows/pass0_encode.vert.spv.msl b/data/shaders/ntsc-md-rainbows/pass0_encode.vert.spv.msl deleted file mode 100644 index 9eb466e..0000000 --- a/data/shaders/ntsc-md-rainbows/pass0_encode.vert.spv.msl +++ /dev/null @@ -1,63 +0,0 @@ -#pragma clang diagnostic ignored "-Wmissing-prototypes" -#pragma clang diagnostic ignored "-Wmissing-braces" - -#include -#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 5fc1317..0000000 Binary files a/data/shaders/ntsc-md-rainbows/pass1_decode.frag.spv and /dev/null differ 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 4cd25bd..0000000 Binary files a/data/shaders/ntsc-md-rainbows/pass1_decode.vert.spv and /dev/null differ diff --git a/data/shaders/ntsc-md-rainbows/pass1_decode.vert.spv.msl b/data/shaders/ntsc-md-rainbows/pass1_decode.vert.spv.msl deleted file mode 100644 index 9eb466e..0000000 --- a/data/shaders/ntsc-md-rainbows/pass1_decode.vert.spv.msl +++ /dev/null @@ -1,63 +0,0 @@ -#pragma clang diagnostic ignored "-Wmissing-prototypes" -#pragma clang diagnostic ignored "-Wmissing-braces" - -#include -#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] = {