feat(gpu): afegir suport SPIRV (Vulkan) per Linux/Windows

- Nou: shaders/sprite.vert|frag, postfx.vert|frag, ball.vert (GLSL)
- Nou: cmake/spv_to_header.cmake — converteix .spv → uint8_t C header
- CMakeLists.txt: bloc non-Apple troba glslc, compila GLSL → SPIRV en
  build-time i genera headers embeguts a build/generated_shaders/
- gpu_context.cpp: MSL|METALLIB en Apple, SPIRV en la resta
- gpu_pipeline.cpp: createShaderSPIRV() + branques #ifdef __APPLE__
  per sprite/ball/postfx pipelines
- Corregeix crash a engine.cpp:821 (Windows/Linux) causat per pipelines
  null quan init() fallava en no trobar suport MSL

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-20 09:47:36 +01:00
parent 5c0d0479ad
commit 6ffe7594ab
9 changed files with 201 additions and 5 deletions

View File

@@ -6,6 +6,15 @@
#include <cstddef> // offsetof
#include <cstring> // strlen
#ifndef __APPLE__
// Generated at build time by CMake + glslc (see cmake/spv_to_header.cmake)
#include "sprite_vert_spv.h"
#include "sprite_frag_spv.h"
#include "postfx_vert_spv.h"
#include "postfx_frag_spv.h"
#include "ball_vert_spv.h"
#endif
// ============================================================================
// MSL Shaders (Metal Shading Language, macOS)
// ============================================================================
@@ -197,18 +206,32 @@ bool GpuPipeline::init(SDL_GPUDevice* device,
SDL_GPUTextureFormat target_format,
SDL_GPUTextureFormat offscreen_format) {
SDL_GPUShaderFormat supported = SDL_GetGPUShaderFormats(device);
#ifdef __APPLE__
if (!(supported & SDL_GPU_SHADERFORMAT_MSL)) {
SDL_Log("GpuPipeline: device does not support MSL shaders (format mask=%u)", supported);
SDL_Log("GpuPipeline: MSL not supported (format mask=%u)", supported);
return false;
}
#else
if (!(supported & SDL_GPU_SHADERFORMAT_SPIRV)) {
SDL_Log("GpuPipeline: SPIRV not supported (format mask=%u)", supported);
return false;
}
#endif
// ----------------------------------------------------------------
// Sprite pipeline
// ----------------------------------------------------------------
#ifdef __APPLE__
SDL_GPUShader* sprite_vert = createShader(device, kSpriteVertMSL, "sprite_vs",
SDL_GPU_SHADERSTAGE_VERTEX, 0, 0);
SDL_GPUShader* sprite_frag = createShader(device, kSpriteFragMSL, "sprite_fs",
SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 0);
#else
SDL_GPUShader* sprite_vert = createShaderSPIRV(device, ksprite_vert_spv, ksprite_vert_spv_size,
"main", SDL_GPU_SHADERSTAGE_VERTEX, 0, 0);
SDL_GPUShader* sprite_frag = createShaderSPIRV(device, ksprite_frag_spv, ksprite_frag_spv_size,
"main", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 0);
#endif
if (!sprite_vert || !sprite_frag) {
SDL_Log("GpuPipeline: failed to create sprite shaders");
if (sprite_vert) SDL_ReleaseGPUShader(device, sprite_vert);
@@ -284,10 +307,17 @@ bool GpuPipeline::init(SDL_GPUDevice* device,
// Fragment: sprite_fs (same texture+color blend as sprite pipeline)
// Targets: offscreen (same as sprite pipeline)
// ----------------------------------------------------------------
#ifdef __APPLE__
SDL_GPUShader* ball_vert = createShader(device, kBallInstancedVertMSL, "ball_instanced_vs",
SDL_GPU_SHADERSTAGE_VERTEX, 0, 0);
SDL_GPUShader* ball_frag = createShader(device, kSpriteFragMSL, "sprite_fs",
SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 0);
#else
SDL_GPUShader* ball_vert = createShaderSPIRV(device, kball_vert_spv, kball_vert_spv_size,
"main", SDL_GPU_SHADERSTAGE_VERTEX, 0, 0);
SDL_GPUShader* ball_frag = createShaderSPIRV(device, ksprite_frag_spv, ksprite_frag_spv_size,
"main", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 0);
#endif
if (!ball_vert || !ball_frag) {
SDL_Log("GpuPipeline: failed to create ball instanced shaders");
if (ball_vert) SDL_ReleaseGPUShader(device, ball_vert);
@@ -356,10 +386,17 @@ bool GpuPipeline::init(SDL_GPUDevice* device,
// ----------------------------------------------------------------
// PostFX pipeline
// ----------------------------------------------------------------
#ifdef __APPLE__
SDL_GPUShader* postfx_vert = createShader(device, kPostFXVertMSL, "postfx_vs",
SDL_GPU_SHADERSTAGE_VERTEX, 0, 0);
SDL_GPUShader* postfx_frag = createShader(device, kPostFXFragMSL, "postfx_fs",
SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 1);
#else
SDL_GPUShader* postfx_vert = createShaderSPIRV(device, kpostfx_vert_spv, kpostfx_vert_spv_size,
"main", SDL_GPU_SHADERSTAGE_VERTEX, 0, 0);
SDL_GPUShader* postfx_frag = createShaderSPIRV(device, kpostfx_frag_spv, kpostfx_frag_spv_size,
"main", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 1);
#endif
if (!postfx_vert || !postfx_frag) {
SDL_Log("GpuPipeline: failed to create postfx shaders");
if (postfx_vert) SDL_ReleaseGPUShader(device, postfx_vert);
@@ -406,6 +443,30 @@ void GpuPipeline::destroy(SDL_GPUDevice* device) {
if (postfx_pipeline_) { SDL_ReleaseGPUGraphicsPipeline(device, postfx_pipeline_); postfx_pipeline_ = nullptr; }
}
SDL_GPUShader* GpuPipeline::createShaderSPIRV(SDL_GPUDevice* device,
const uint8_t* spv_code,
size_t spv_size,
const char* entrypoint,
SDL_GPUShaderStage stage,
Uint32 num_samplers,
Uint32 num_uniform_buffers,
Uint32 num_storage_buffers) {
SDL_GPUShaderCreateInfo info = {};
info.code = spv_code;
info.code_size = spv_size;
info.entrypoint = entrypoint;
info.format = SDL_GPU_SHADERFORMAT_SPIRV;
info.stage = stage;
info.num_samplers = num_samplers;
info.num_storage_textures = 0;
info.num_storage_buffers = num_storage_buffers;
info.num_uniform_buffers = num_uniform_buffers;
SDL_GPUShader* shader = SDL_CreateGPUShader(device, &info);
if (!shader)
SDL_Log("GpuPipeline: SPIRV shader '%s' failed: %s", entrypoint, SDL_GetError());
return shader;
}
SDL_GPUShader* GpuPipeline::createShader(SDL_GPUDevice* device,
const char* msl_source,
const char* entrypoint,