feat(macos): suport Metal per GpuShaderPreset via spirv-cross
- CMakeLists: bloc if(APPLE) que transpila .spv → .spv.msl amb spirv-cross - gpu_shader_preset: carrega MSL (main0) a macOS, SPIR-V (main) a la resta - Afegeix null-terminator als buffers MSL (SDL3 els tracta com C-string) - README: secció de dependències de shaders per plataforma (Vulkan SDK, spirv-cross) - Inclou .spv.msl generats per ntsc-md-rainbows Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -94,6 +94,33 @@ if(NOT APPLE)
|
|||||||
endif()
|
endif()
|
||||||
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)
|
# 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)
|
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")
|
list(REMOVE_ITEM SOURCE_FILES "${CMAKE_SOURCE_DIR}/source/main_old.cpp")
|
||||||
@@ -138,6 +165,10 @@ if(NOT APPLE)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(APPLE AND TARGET data_shader_msls)
|
||||||
|
add_dependencies(${PROJECT_NAME} data_shader_msls)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Tool: pack_resources
|
# Tool: pack_resources
|
||||||
add_executable(pack_resources tools/pack_resources.cpp source/resource_pack.cpp)
|
add_executable(pack_resources tools/pack_resources.cpp source/resource_pack.cpp)
|
||||||
target_include_directories(pack_resources PRIVATE ${CMAKE_SOURCE_DIR}/source)
|
target_include_directories(pack_resources PRIVATE ${CMAKE_SOURCE_DIR}/source)
|
||||||
|
|||||||
87
README.md
87
README.md
@@ -531,49 +531,92 @@ vibe3_physics/
|
|||||||
## 🔧 Requisitos del Sistema
|
## 🔧 Requisitos del Sistema
|
||||||
|
|
||||||
- **SDL3** (Simple DirectMedia Layer 3)
|
- **SDL3** (Simple DirectMedia Layer 3)
|
||||||
|
- **SDL3_ttf** (fuentes TTF para SDL3)
|
||||||
- **C++20** compatible compiler (GCC 10+, Clang 12+, MSVC 2019+)
|
- **C++20** compatible compiler (GCC 10+, Clang 12+, MSVC 2019+)
|
||||||
- **CMake 3.20+** o **Make**
|
- **CMake 3.20+**
|
||||||
- **Plataforma**: Windows, Linux, macOS
|
- **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
|
```bash
|
||||||
# Usando vcpkg
|
# SDL3 + SDL3_ttf
|
||||||
vcpkg install sdl3
|
brew install sdl3 sdl3_ttf
|
||||||
|
|
||||||
# O compilar desde fuente
|
# Vulkan SDK (incluye glslc para compilar shaders)
|
||||||
git clone https://github.com/libsdl-org/SDL
|
# Descargar desde https://vulkan.lunarg.com/ o via Homebrew:
|
||||||
cd SDL
|
brew install --cask vulkan-sdk
|
||||||
mkdir build && cd build
|
|
||||||
cmake ..
|
# spirv-cross (transpilación SPIR-V → MSL para Metal)
|
||||||
make
|
brew install spirv-cross
|
||||||
sudo make install
|
```
|
||||||
|
|
||||||
|
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/<version>/macOS/bin:$PATH"
|
||||||
|
source ~/.zshrc
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Linux (Ubuntu/Debian)
|
#### 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)
|
#### Linux (Arch)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo pacman -S sdl3
|
sudo pacman -S sdl3 sdl3_ttf vulkan-devel glslang shaderc
|
||||||
```
|
```
|
||||||
|
|
||||||
#### macOS
|
#### Windows (MinGW / MSYS2)
|
||||||
|
|
||||||
```bash
|
```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
|
## 🚀 Compilación
|
||||||
|
|
||||||
### Opción 1: CMake (Recomendado)
|
### CMake (todas las plataformas)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mkdir -p build && cd build
|
mkdir -p build && cd build
|
||||||
@@ -581,7 +624,9 @@ cmake ..
|
|||||||
cmake --build .
|
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
|
```bash
|
||||||
make
|
make
|
||||||
|
|||||||
61
data/shaders/ntsc-md-rainbows/pass0_encode.frag.spv.msl
Normal file
61
data/shaders/ntsc-md-rainbows/pass0_encode.frag.spv.msl
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#include <metal_stdlib>
|
||||||
|
#include <simd/simd.h>
|
||||||
|
|
||||||
|
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<float> 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;
|
||||||
|
}
|
||||||
|
|
||||||
63
data/shaders/ntsc-md-rainbows/pass0_encode.vert.spv.msl
Normal file
63
data/shaders/ntsc-md-rainbows/pass0_encode.vert.spv.msl
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#pragma clang diagnostic ignored "-Wmissing-prototypes"
|
||||||
|
#pragma clang diagnostic ignored "-Wmissing-braces"
|
||||||
|
|
||||||
|
#include <metal_stdlib>
|
||||||
|
#include <simd/simd.h>
|
||||||
|
|
||||||
|
using namespace metal;
|
||||||
|
|
||||||
|
template<typename T, size_t Num>
|
||||||
|
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<float2, 3> _18 = spvUnsafeArray<float2, 3>({ float2(-1.0), float2(3.0, -1.0), float2(-1.0, 3.0) });
|
||||||
|
constant spvUnsafeArray<float2, 3> _26 = spvUnsafeArray<float2, 3>({ 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;
|
||||||
|
}
|
||||||
|
|
||||||
304
data/shaders/ntsc-md-rainbows/pass1_decode.frag.spv.msl
Normal file
304
data/shaders/ntsc-md-rainbows/pass1_decode.frag.spv.msl
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
#include <metal_stdlib>
|
||||||
|
#include <simd/simd.h>
|
||||||
|
|
||||||
|
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<float> 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;
|
||||||
|
}
|
||||||
|
|
||||||
63
data/shaders/ntsc-md-rainbows/pass1_decode.vert.spv.msl
Normal file
63
data/shaders/ntsc-md-rainbows/pass1_decode.vert.spv.msl
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#pragma clang diagnostic ignored "-Wmissing-prototypes"
|
||||||
|
#pragma clang diagnostic ignored "-Wmissing-braces"
|
||||||
|
|
||||||
|
#include <metal_stdlib>
|
||||||
|
#include <simd/simd.h>
|
||||||
|
|
||||||
|
using namespace metal;
|
||||||
|
|
||||||
|
template<typename T, size_t Num>
|
||||||
|
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<float2, 3> _18 = spvUnsafeArray<float2, 3>({ float2(-1.0), float2(3.0, -1.0), float2(-1.0, 3.0) });
|
||||||
|
constant spvUnsafeArray<float2, 3> _26 = spvUnsafeArray<float2, 3>({ 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;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -110,6 +110,14 @@ SDL_GPUGraphicsPipeline* GpuShaderPreset::buildPassPipeline(SDL_GPUDevice* devic
|
|||||||
const std::string& vert_spv_path,
|
const std::string& vert_spv_path,
|
||||||
const std::string& frag_spv_path,
|
const std::string& frag_spv_path,
|
||||||
SDL_GPUTextureFormat target_fmt) {
|
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 vert_spv = readFile(vert_spv_path);
|
||||||
auto frag_spv = readFile(frag_spv_path);
|
auto frag_spv = readFile(frag_spv_path);
|
||||||
if (vert_spv.empty()) {
|
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());
|
SDL_Log("GpuShaderPreset: cannot read %s", frag_spv_path.c_str());
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
#ifdef __APPLE__
|
||||||
|
vert_spv.push_back(0);
|
||||||
|
frag_spv.push_back(0);
|
||||||
|
#endif
|
||||||
|
|
||||||
SDL_GPUShaderCreateInfo vert_info = {};
|
SDL_GPUShaderCreateInfo vert_info = {};
|
||||||
vert_info.code = vert_spv.data();
|
vert_info.code = vert_spv.data();
|
||||||
vert_info.code_size = vert_spv.size();
|
vert_info.code_size = vert_spv.size();
|
||||||
vert_info.entrypoint = "main";
|
vert_info.entrypoint = kEntry;
|
||||||
vert_info.format = SDL_GPU_SHADERFORMAT_SPIRV;
|
vert_info.format = kFmt;
|
||||||
vert_info.stage = SDL_GPU_SHADERSTAGE_VERTEX;
|
vert_info.stage = SDL_GPU_SHADERSTAGE_VERTEX;
|
||||||
vert_info.num_samplers = 0;
|
vert_info.num_samplers = 0;
|
||||||
vert_info.num_uniform_buffers = 0;
|
vert_info.num_uniform_buffers = 0;
|
||||||
@@ -133,8 +145,8 @@ SDL_GPUGraphicsPipeline* GpuShaderPreset::buildPassPipeline(SDL_GPUDevice* devic
|
|||||||
SDL_GPUShaderCreateInfo frag_info = {};
|
SDL_GPUShaderCreateInfo frag_info = {};
|
||||||
frag_info.code = frag_spv.data();
|
frag_info.code = frag_spv.data();
|
||||||
frag_info.code_size = frag_spv.size();
|
frag_info.code_size = frag_spv.size();
|
||||||
frag_info.entrypoint = "main";
|
frag_info.entrypoint = kEntry;
|
||||||
frag_info.format = SDL_GPU_SHADERFORMAT_SPIRV;
|
frag_info.format = kFmt;
|
||||||
frag_info.stage = SDL_GPU_SHADERSTAGE_FRAGMENT;
|
frag_info.stage = SDL_GPU_SHADERSTAGE_FRAGMENT;
|
||||||
frag_info.num_samplers = 1;
|
frag_info.num_samplers = 1;
|
||||||
frag_info.num_uniform_buffers = 1;
|
frag_info.num_uniform_buffers = 1;
|
||||||
@@ -201,8 +213,13 @@ bool GpuShaderPreset::load(SDL_GPUDevice* device,
|
|||||||
bool is_last = (i == n - 1);
|
bool is_last = (i == n - 1);
|
||||||
SDL_GPUTextureFormat target_fmt = is_last ? swapchain_fmt : inter_fmt;
|
SDL_GPUTextureFormat target_fmt = is_last ? swapchain_fmt : inter_fmt;
|
||||||
|
|
||||||
std::string vert_spv = dir + "/" + descs_[i].vert_name + ".spv";
|
#ifdef __APPLE__
|
||||||
std::string frag_spv = dir + "/" + descs_[i].frag_name + ".spv";
|
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);
|
passes_[i].pipeline = buildPassPipeline(device, vert_spv, frag_spv, target_fmt);
|
||||||
if (!passes_[i].pipeline) {
|
if (!passes_[i].pipeline) {
|
||||||
|
|||||||
Reference in New Issue
Block a user