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:
2026-03-20 13:54:05 +01:00
parent f272bab296
commit 5b4a970157
7 changed files with 611 additions and 27 deletions

View File

@@ -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) {