diff --git a/CMakeLists.txt b/CMakeLists.txt index b2c7e72..e51365c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,6 +94,33 @@ if(NOT APPLE) 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) file(GLOB SOURCE_FILES source/*.cpp source/external/*.cpp source/boids_mgr/*.cpp source/gpu/*.cpp source/input/*.cpp source/scene/*.cpp source/shapes/*.cpp source/shapes_mgr/*.cpp source/state/*.cpp source/themes/*.cpp source/text/*.cpp source/ui/*.cpp) list(REMOVE_ITEM SOURCE_FILES "${CMAKE_SOURCE_DIR}/source/main_old.cpp") @@ -138,6 +165,10 @@ if(NOT APPLE) endif() endif() +if(APPLE AND TARGET data_shader_msls) + add_dependencies(${PROJECT_NAME} data_shader_msls) +endif() + # Tool: pack_resources add_executable(pack_resources tools/pack_resources.cpp source/resource_pack.cpp) target_include_directories(pack_resources PRIVATE ${CMAKE_SOURCE_DIR}/source) diff --git a/README.md b/README.md index c0dc6dd..48dc42c 100644 --- a/README.md +++ b/README.md @@ -531,49 +531,92 @@ vibe3_physics/ ## 🔧 Requisitos del Sistema - **SDL3** (Simple DirectMedia Layer 3) +- **SDL3_ttf** (fuentes TTF para SDL3) - **C++20** compatible compiler (GCC 10+, Clang 12+, MSVC 2019+) -- **CMake 3.20+** o **Make** +- **CMake 3.20+** - **Plataforma**: Windows, Linux, macOS -### Instalación de SDL3 +### 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`. + +| Plataforma | Backend GPU | Herramienta de shaders | Resultado | +|------------|-------------|------------------------|-----------| +| Linux | Vulkan | `glslc` (Vulkan SDK) | `.spv` | +| Windows | Vulkan | `glslc` (Vulkan SDK) | `.spv` | +| macOS | Metal | `glslc` + `spirv-cross`| `.spv.msl`| + +--- + +### Instalación de dependencias + +#### macOS -#### Windows (MinGW) ```bash -# Usando vcpkg -vcpkg install sdl3 +# SDL3 + SDL3_ttf +brew install sdl3 sdl3_ttf -# O compilar desde fuente -git clone https://github.com/libsdl-org/SDL -cd SDL -mkdir build && cd build -cmake .. -make -sudo make install +# Vulkan SDK (incluye glslc para compilar shaders) +# Descargar desde https://vulkan.lunarg.com/ o via Homebrew: +brew install --cask vulkan-sdk + +# spirv-cross (transpilación SPIR-V → MSL para Metal) +brew install spirv-cross +``` + +Tras instalar el Vulkan SDK es posible que necesites añadir `glslc` al PATH: +```bash +# Añadir a ~/.zshrc o ~/.bash_profile (ajusta la versión): +export PATH="$HOME/VulkanSDK//macOS/bin:$PATH" +source ~/.zshrc ``` #### Linux (Ubuntu/Debian) -```bash -sudo apt update -sudo apt install libsdl3-dev -# Si no está disponible, compilar desde fuente +```bash +# SDL3 + SDL3_ttf +sudo apt update +sudo apt install libsdl3-dev libsdl3-ttf-dev + +# Vulkan SDK (incluye glslc) +# Opción A: paquete del sistema +sudo apt install glslc + +# Opción B: Vulkan SDK completo (recomendado para versión reciente) +wget -qO- https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add - +sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan.list \ + https://packages.lunarg.com/vulkan/lunarg-vulkan-focal.list +sudo apt update +sudo apt install vulkan-sdk ``` #### Linux (Arch) + ```bash -sudo pacman -S sdl3 +sudo pacman -S sdl3 sdl3_ttf vulkan-devel glslang shaderc ``` -#### macOS +#### Windows (MinGW / MSYS2) + ```bash -brew install sdl3 +# En MSYS2 shell: +pacman -S mingw-w64-x86_64-SDL3 mingw-w64-x86_64-SDL3_ttf + +# Vulkan SDK: descargar el instalador desde https://vulkan.lunarg.com/ +# El instalador añade glslc al PATH automáticamente. +``` + +Con Visual Studio + vcpkg: +```bash +vcpkg install sdl3 sdl3-ttf +# Instalar Vulkan SDK desde https://vulkan.lunarg.com/ (incluye glslc) ``` --- ## 🚀 Compilación -### Opción 1: CMake (Recomendado) +### CMake (todas las plataformas) ```bash mkdir -p build && cd build @@ -581,7 +624,9 @@ cmake .. cmake --build . ``` -### Opción 2: Make directo +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í). + +### Make directo (Linux/macOS) ```bash make diff --git a/data/shaders/ntsc-md-rainbows/pass0_encode.frag.spv.msl b/data/shaders/ntsc-md-rainbows/pass0_encode.frag.spv.msl new file mode 100644 index 0000000..ea468df --- /dev/null +++ b/data/shaders/ntsc-md-rainbows/pass0_encode.frag.spv.msl @@ -0,0 +1,61 @@ +#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.spv.msl b/data/shaders/ntsc-md-rainbows/pass0_encode.vert.spv.msl new file mode 100644 index 0000000..9eb466e --- /dev/null +++ b/data/shaders/ntsc-md-rainbows/pass0_encode.vert.spv.msl @@ -0,0 +1,63 @@ +#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.spv.msl b/data/shaders/ntsc-md-rainbows/pass1_decode.frag.spv.msl new file mode 100644 index 0000000..7f8c003 --- /dev/null +++ b/data/shaders/ntsc-md-rainbows/pass1_decode.frag.spv.msl @@ -0,0 +1,304 @@ +#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.spv.msl b/data/shaders/ntsc-md-rainbows/pass1_decode.vert.spv.msl new file mode 100644 index 0000000..9eb466e --- /dev/null +++ b/data/shaders/ntsc-md-rainbows/pass1_decode.vert.spv.msl @@ -0,0 +1,63 @@ +#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/source/gpu/gpu_shader_preset.cpp b/source/gpu/gpu_shader_preset.cpp index fe3c0e3..bfb5222 100644 --- a/source/gpu/gpu_shader_preset.cpp +++ b/source/gpu/gpu_shader_preset.cpp @@ -110,6 +110,14 @@ SDL_GPUGraphicsPipeline* GpuShaderPreset::buildPassPipeline(SDL_GPUDevice* devic 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()) { @@ -120,12 +128,16 @@ SDL_GPUGraphicsPipeline* GpuShaderPreset::buildPassPipeline(SDL_GPUDevice* devic 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 = "main"; - vert_info.format = SDL_GPU_SHADERFORMAT_SPIRV; + 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; @@ -133,8 +145,8 @@ SDL_GPUGraphicsPipeline* GpuShaderPreset::buildPassPipeline(SDL_GPUDevice* devic SDL_GPUShaderCreateInfo frag_info = {}; frag_info.code = frag_spv.data(); frag_info.code_size = frag_spv.size(); - frag_info.entrypoint = "main"; - frag_info.format = SDL_GPU_SHADERFORMAT_SPIRV; + 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; @@ -201,8 +213,13 @@ bool GpuShaderPreset::load(SDL_GPUDevice* device, bool is_last = (i == n - 1); SDL_GPUTextureFormat target_fmt = is_last ? swapchain_fmt : inter_fmt; - std::string vert_spv = dir + "/" + descs_[i].vert_name + ".spv"; - std::string frag_spv = dir + "/" + descs_[i].frag_name + ".spv"; +#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) {