refactor(sdl3gpu): extreure shaders MSL i helpers a headers compartits (CONV-03)

Els dos pipelines GPU (PostFX i CrtPi) tenien:
- Raw strings MSL incrustades al .cpp amb NOLINTBEGIN(readability-
  identifier-naming) envoltant ~150 línies cadascun.
- Funcions createShaderMSL / createShaderSPIRV duplicades byte a byte
  en cada .cpp (DUP-04), amb un comentari admetent-ho.
- El vertex shader postfx_vs escrit dues voltes, idèntic (DUP-05).

S'introdueixen:
- source/core/rendering/sdl3gpu/shader_factory.hpp amb els dos helpers
  inline, per a reutilitzar des de qualsevol pipeline.
- source/core/rendering/sdl3gpu/msl/ amb un header per shader
  (postfx_vert compartit, postfx_frag, crtpi_frag); cada constant es
  diu kCamelCase i cau per defecte sota el project naming, així que
  els blocs NOLINT desapareixen (PATCH-07).
- Suppresió a cppcheck per a sdl3gpu/msl/* (és MSL, no C++).

Els .cpp es redueixen ~190 línies cada un i queden només amb la
lògica del pipeline.

Build + smoke test de 5s sota xvfb OK (exit 124).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
JailDesignerandClaude Opus 4.7 committed 2026-04-20 09:28:52 +02:00
1 parent d479475dcf
commit 5f46fb22a4
8 files changed
+377 -395

No files matched your search

@@ -3,206 +3,17 @@
#include <cstring>
#include <iostream>
#include "core/rendering/sdl3gpu/shader_factory.hpp"
#include "core/rendering/sdl3gpu/viewport.hpp"
#ifndef __APPLE__
#ifdef __APPLE__
#include "core/rendering/sdl3gpu/msl/crtpi_frag.msl.h"
#include "core/rendering/sdl3gpu/msl/postfx_vert.msl.h"
#else
#include "core/rendering/sdl3gpu/spv/crtpi_frag_spv.h"
#include "core/rendering/sdl3gpu/spv/postfx_vert_spv.h"
#endif
namespace {
#ifdef __APPLE__
// NOLINTBEGIN(readability-identifier-naming)
const char* CRTPI_VERT_MSL_ = R"(
#include <metal_stdlib>
using namespace metal;
struct PostVOut {
float4 pos [[position]];
float2 uv;
};
vertex PostVOut postfx_vs(uint vid [[vertex_id]]) {
const float2 positions[3] = { {-1.0, -1.0}, {3.0, -1.0}, {-1.0, 3.0} };
const float2 uvs[3] = { { 0.0, 1.0}, {2.0, 1.0}, { 0.0,-1.0} };
PostVOut out;
out.pos = float4(positions[vid], 0.0, 1.0);
out.uv = uvs[vid];
return out;
}
)";
const char* CRTPI_FRAG_MSL_ = R"(
#include <metal_stdlib>
using namespace metal;
struct PostVOut {
float4 pos [[position]];
float2 uv;
};
struct CrtPiUniforms {
float scanline_weight;
float scanline_gap_brightness;
float bloom_factor;
float input_gamma;
float output_gamma;
float mask_brightness;
float curvature_x;
float curvature_y;
int mask_type;
int enable_scanlines;
int enable_multisample;
int enable_gamma;
int enable_curvature;
int enable_sharper;
float texture_width;
float texture_height;
};
static float2 crtpi_distort(float2 coord, float2 screen_scale, float cx, float cy) {
float2 curvature = float2(cx, cy);
float2 barrel_scale = 1.0f - (0.23f * curvature);
coord *= screen_scale;
coord -= 0.5f;
float rsq = coord.x * coord.x + coord.y * coord.y;
coord += coord * (curvature * rsq);
coord *= barrel_scale;
if (abs(coord.x) >= 0.5f || abs(coord.y) >= 0.5f) { return float2(-1.0f); }
coord += 0.5f;
coord /= screen_scale;
return coord;
}
static float crtpi_scan_weight(float dist, float sw, float gap) {
return max(1.0f - dist * dist * sw, gap);
}
static float crtpi_scan_line(float dy, float filter_w, float sw, float gap, bool ms) {
float w = crtpi_scan_weight(dy, sw, gap);
if (ms) {
w += crtpi_scan_weight(dy - filter_w, sw, gap);
w += crtpi_scan_weight(dy + filter_w, sw, gap);
w *= 0.3333333f;
}
return w;
}
fragment float4 crtpi_fs(PostVOut in [[stage_in]],
texture2d<float> tex [[texture(0)]],
sampler samp [[sampler(0)]],
constant CrtPiUniforms& u [[buffer(0)]]) {
float2 tex_size = float2(u.texture_width, u.texture_height);
float filter_width = (768.0f / u.texture_height) / 3.0f;
float2 texcoord = in.uv;
if (u.enable_curvature != 0) {
texcoord = crtpi_distort(texcoord, float2(1.0f, 1.0f), u.curvature_x, u.curvature_y);
if (texcoord.x < 0.0f) { return float4(0.0f, 0.0f, 0.0f, 1.0f); }
}
float2 coord_in_pixels = texcoord * tex_size;
float2 tc;
float scan_weight;
if (u.enable_sharper != 0) {
float2 temp = floor(coord_in_pixels) + 0.5f;
tc = temp / tex_size;
float2 deltas = coord_in_pixels - temp;
scan_weight = crtpi_scan_line(deltas.y, filter_width, u.scanline_weight, u.scanline_gap_brightness, u.enable_multisample != 0);
float2 signs = sign(deltas);
deltas.x *= 2.0f;
deltas = deltas * deltas;
deltas.y = deltas.y * deltas.y;
deltas.x *= 0.5f;
deltas.y *= 8.0f;
deltas /= tex_size;
deltas *= signs;
tc = tc + deltas;
} else {
float temp_y = floor(coord_in_pixels.y) + 0.5f;
float y_coord = temp_y / tex_size.y;
float dy = coord_in_pixels.y - temp_y;
scan_weight = crtpi_scan_line(dy, filter_width, u.scanline_weight, u.scanline_gap_brightness, u.enable_multisample != 0);
float sign_y = sign(dy);
dy = dy * dy;
dy = dy * dy;
dy *= 8.0f;
dy /= tex_size.y;
dy *= sign_y;
tc = float2(texcoord.x, y_coord + dy);
}
float3 colour = tex.sample(samp, tc).rgb;
if (u.enable_scanlines != 0) {
if (u.enable_gamma != 0) { colour = pow(colour, float3(u.input_gamma)); }
colour *= scan_weight * u.bloom_factor;
if (u.enable_gamma != 0) { colour = pow(colour, float3(1.0f / u.output_gamma)); }
}
if (u.mask_type == 1) {
float wm = fract(in.pos.x * 0.5f);
float3 mask = (wm < 0.5f) ? float3(u.mask_brightness, 1.0f, u.mask_brightness)
: float3(1.0f, u.mask_brightness, 1.0f);
colour *= mask;
} else if (u.mask_type == 2) {
float wm = fract(in.pos.x * 0.3333333f);
float3 mask = float3(u.mask_brightness);
if (wm < 0.3333333f) mask.x = 1.0f;
else if (wm < 0.6666666f) mask.y = 1.0f;
else mask.z = 1.0f;
colour *= mask;
}
return float4(colour, 1.0f);
}
)";
// NOLINTEND(readability-identifier-naming)
#endif
// Helpers per a crear shaders. Duplicats en cada pipeline per mantindre els
// fitxers autònoms — són funcions trivials i no val la pena un header compartit.
#ifdef __APPLE__
auto createShaderMSL(SDL_GPUDevice* device,
const char* msl_source,
const char* entrypoint,
SDL_GPUShaderStage stage,
Uint32 num_samplers,
Uint32 num_uniform_buffers) -> SDL_GPUShader* {
SDL_GPUShaderCreateInfo info{};
info.code = reinterpret_cast<const Uint8*>(msl_source);
info.code_size = std::strlen(msl_source) + 1;
info.entrypoint = entrypoint;
info.format = SDL_GPU_SHADERFORMAT_MSL;
info.stage = stage;
info.num_samplers = num_samplers;
info.num_uniform_buffers = num_uniform_buffers;
return SDL_CreateGPUShader(device, &info);
}
#else
auto createShaderSPIRV(SDL_GPUDevice* device,
const std::uint8_t* spv_code,
std::size_t spv_size,
const char* entrypoint,
SDL_GPUShaderStage stage,
Uint32 num_samplers,
Uint32 num_uniform_buffers) -> SDL_GPUShader* {
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_uniform_buffers = num_uniform_buffers;
return SDL_CreateGPUShader(device, &info);
}
#endif
} // namespace
namespace Rendering {
CrtPiPipeline::~CrtPiPipeline() { cleanup(); }
@@ -211,8 +22,8 @@ namespace Rendering {
device_ = device;
#ifdef __APPLE__
SDL_GPUShader* vert = createShaderMSL(device_, CRTPI_VERT_MSL_, "postfx_vs", SDL_GPU_SHADERSTAGE_VERTEX, 0, 0);
SDL_GPUShader* frag = createShaderMSL(device_, CRTPI_FRAG_MSL_, "crtpi_fs", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 1);
SDL_GPUShader* vert = createShaderMSL(device_, Msl::kPostfxVert, "postfx_vs", SDL_GPU_SHADERSTAGE_VERTEX, 0, 0);
SDL_GPUShader* frag = createShaderMSL(device_, Msl::kCrtpiFrag, "crtpi_fs", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 1);
#else
SDL_GPUShader* vert = createShaderSPIRV(device_, kpostfx_vert_spv, kpostfx_vert_spv_size, "main", SDL_GPU_SHADERSTAGE_VERTEX, 0, 0);
SDL_GPUShader* frag = createShaderSPIRV(device_, kcrtpi_frag_spv, kcrtpi_frag_spv_size, "main", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 1);