audita NOLINT/cppcheck-suppress: refactor i justifica residuals

This commit is contained in:
2026-05-17 09:18:08 +02:00
parent 91add6f2fe
commit e887b77dcb
7 changed files with 292 additions and 383 deletions
+2 -2
View File
@@ -61,13 +61,13 @@ void MovingSprite::render() {
} }
// Obtiene el valor de la variable // Obtiene el valor de la variable
// cppcheck-suppress duplInheritedMember // cppcheck-suppress duplInheritedMember ; shadow intencional: vegeu movingsprite.h
auto MovingSprite::getPosX() const -> float { auto MovingSprite::getPosX() const -> float {
return x_; return x_;
} }
// Obtiene el valor de la variable // Obtiene el valor de la variable
// cppcheck-suppress duplInheritedMember // cppcheck-suppress duplInheritedMember ; shadow intencional: vegeu movingsprite.h
auto MovingSprite::getPosY() const -> float { auto MovingSprite::getPosY() const -> float {
return y_; return y_;
} }
+4 -4
View File
@@ -16,9 +16,9 @@ class MovingSprite : public Sprite {
void clear(); // Reinicia todas las variables void clear(); // Reinicia todas las variables
void render() override; // Muestra el sprite por pantalla void render() override; // Muestra el sprite por pantalla
// cppcheck-suppress duplInheritedMember // cppcheck-suppress duplInheritedMember ; shadow intencional: Sprite::getPosX retorna int (sprites estàtics), MovingSprite::getPosX retorna float (sub-pixel). No s'accedeix via Sprite*: la jerarquia de joc treballa amb el tipus concret
[[nodiscard]] auto getPosX() const -> float; // Obten el valor de la variable [[nodiscard]] auto getPosX() const -> float; // Obten el valor de la variable
// cppcheck-suppress duplInheritedMember // cppcheck-suppress duplInheritedMember ; shadow intencional: vegeu nota a getPosX
[[nodiscard]] auto getPosY() const -> float; // Obten el valor de la variable [[nodiscard]] auto getPosY() const -> float; // Obten el valor de la variable
[[nodiscard]] auto getVelX() const -> float; // Obten el valor de la variable [[nodiscard]] auto getVelX() const -> float; // Obten el valor de la variable
@@ -64,9 +64,9 @@ class MovingSprite : public Sprite {
[[nodiscard]] auto getIncX() const -> int; // Devuelve el incremento en el eje X en pixels [[nodiscard]] auto getIncX() const -> int; // Devuelve el incremento en el eje X en pixels
protected: protected:
// cppcheck-suppress duplInheritedMember // cppcheck-suppress duplInheritedMember ; shadow intencional: Sprite::x_ és int (posició entera per a sprites estàtics), MovingSprite::x_ és float (sub-pixel per a entitats mòbils). No s'accedeix via punter a Sprite*
float x_; // Posición en el eje X (sub-pixel; Sprite::x_ es int) float x_; // Posición en el eje X (sub-pixel; Sprite::x_ es int)
// cppcheck-suppress duplInheritedMember // cppcheck-suppress duplInheritedMember ; shadow intencional: vegeu nota a x_
float y_; // Posición en el eje Y (sub-pixel; Sprite::y_ es int) float y_; // Posición en el eje Y (sub-pixel; Sprite::y_ es int)
float x_prev_; // Posición anterior en el eje X float x_prev_; // Posición anterior en el eje X
+256 -357
View File
@@ -20,7 +20,6 @@
// MSL shaders (Metal Shading Language) — macOS // MSL shaders (Metal Shading Language) — macOS
// ============================================================================ // ============================================================================
// NOLINTBEGIN(readability-identifier-naming)
static const char* POSTFX_VERT_MSL = R"( static const char* POSTFX_VERT_MSL = R"(
#include <metal_stdlib> #include <metal_stdlib>
using namespace metal; using namespace metal;
@@ -360,7 +359,6 @@ fragment float4 crtpi_fs(PostVOut in [[stage_in]],
return float4(colour, 1.0f); return float4(colour, 1.0f);
} }
)"; )";
// NOLINTEND(readability-identifier-naming)
#endif // __APPLE__ #endif // __APPLE__
@@ -525,25 +523,30 @@ namespace Rendering {
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// createPipeline // createPostfxVertexShader — fullscreen-triangle vertex compartit per tots els pipelines
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
auto SDL3GPUShader::createPipeline() -> bool { // NOLINT(readability-function-cognitive-complexity) auto SDL3GPUShader::createPostfxVertexShader() -> SDL_GPUShader* {
const SDL_GPUTextureFormat SWAPCHAIN_FMT = SDL_GetGPUSwapchainTextureFormat(device_, window_);
// ---- PostFX pipeline (scene/scaled → swapchain) ----
#ifdef __APPLE__ #ifdef __APPLE__
SDL_GPUShader* vert = createShaderMSL(device_, POSTFX_VERT_MSL, "postfx_vs", SDL_GPU_SHADERSTAGE_VERTEX, 0, 0); return createShaderMSL(device_, POSTFX_VERT_MSL, "postfx_vs", SDL_GPU_SHADERSTAGE_VERTEX, 0, 0);
SDL_GPUShader* frag = createShaderMSL(device_, POSTFX_FRAG_MSL, "postfx_fs", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 1);
#else #else
SDL_GPUShader* vert = createShaderSPIRV(device_, kpostfx_vert_spv, kpostfx_vert_spv_size, "main", SDL_GPU_SHADERSTAGE_VERTEX, 0, 0); return createShaderSPIRV(device_, kpostfx_vert_spv, kpostfx_vert_spv_size, "main", SDL_GPU_SHADERSTAGE_VERTEX, 0, 0);
SDL_GPUShader* frag = createShaderSPIRV(device_, kpostfx_frag_spv, kpostfx_frag_spv_size, "main", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 1);
#endif #endif
}
if ((vert == nullptr) || (frag == nullptr)) { // ---------------------------------------------------------------------------
SDL_Log("SDL3GPUShader: failed to compile PostFX shaders"); // createPostfxLikePipeline — empaqueta vert(postfx) + frag dado + target en un pipeline.
if (vert != nullptr) { SDL_ReleaseGPUShader(device_, vert); } // Pren ownership de `frag` (el libera abans de retornar).
if (frag != nullptr) { SDL_ReleaseGPUShader(device_, frag); } // ---------------------------------------------------------------------------
return false; auto SDL3GPUShader::createPostfxLikePipeline(SDL_GPUShader* frag, SDL_GPUTextureFormat format, const char* debug_name) -> SDL_GPUGraphicsPipeline* {
if (frag == nullptr) {
SDL_Log("SDL3GPUShader: %s frag shader is null", debug_name);
return nullptr;
}
SDL_GPUShader* vert = createPostfxVertexShader();
if (vert == nullptr) {
SDL_Log("SDL3GPUShader: %s vert shader creation failed", debug_name);
SDL_ReleaseGPUShader(device_, frag);
return nullptr;
} }
SDL_GPUColorTargetBlendState no_blend = {}; SDL_GPUColorTargetBlendState no_blend = {};
@@ -551,145 +554,55 @@ namespace Rendering {
no_blend.enable_color_write_mask = false; no_blend.enable_color_write_mask = false;
SDL_GPUColorTargetDescription color_target = {}; SDL_GPUColorTargetDescription color_target = {};
color_target.format = SWAPCHAIN_FMT; color_target.format = format;
color_target.blend_state = no_blend; color_target.blend_state = no_blend;
SDL_GPUVertexInputState no_input = {}; SDL_GPUVertexInputState no_input = {};
SDL_GPUGraphicsPipelineCreateInfo pipe_info = {}; SDL_GPUGraphicsPipelineCreateInfo info = {};
pipe_info.vertex_shader = vert; info.vertex_shader = vert;
pipe_info.fragment_shader = frag; info.fragment_shader = frag;
pipe_info.vertex_input_state = no_input; info.vertex_input_state = no_input;
pipe_info.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST; info.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST;
pipe_info.target_info.num_color_targets = 1; info.target_info.num_color_targets = 1;
pipe_info.target_info.color_target_descriptions = &color_target; info.target_info.color_target_descriptions = &color_target;
pipeline_ = SDL_CreateGPUGraphicsPipeline(device_, &pipe_info); SDL_GPUGraphicsPipeline* pipeline = SDL_CreateGPUGraphicsPipeline(device_, &info);
SDL_ReleaseGPUShader(device_, vert); SDL_ReleaseGPUShader(device_, vert);
SDL_ReleaseGPUShader(device_, frag); SDL_ReleaseGPUShader(device_, frag);
if (pipeline_ == nullptr) { if (pipeline == nullptr) {
SDL_Log("SDL3GPUShader: PostFX pipeline creation failed: %s", SDL_GetError()); SDL_Log("SDL3GPUShader: %s pipeline creation failed: %s", debug_name, SDL_GetError());
return false;
} }
return pipeline;
}
// ---------------------------------------------------------------------------
// createPipeline — crea els 4 pipelines del flux PostFX
// ---------------------------------------------------------------------------
auto SDL3GPUShader::createPipeline() -> bool {
const SDL_GPUTextureFormat SWAPCHAIN_FMT = SDL_GetGPUSwapchainTextureFormat(device_, window_);
const SDL_GPUTextureFormat OFFSCREEN_FMT = SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM;
// ---- Upscale pipeline (scene → scaled_texture_, nearest) ----
#ifdef __APPLE__ #ifdef __APPLE__
SDL_GPUShader* uvert = createShaderMSL(device_, POSTFX_VERT_MSL, "postfx_vs", SDL_GPU_SHADERSTAGE_VERTEX, 0, 0); SDL_GPUShader* postfx_frag = createShaderMSL(device_, POSTFX_FRAG_MSL, "postfx_fs", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 1);
SDL_GPUShader* ufrag = createShaderMSL(device_, UPSCALE_FRAG_MSL, "upscale_fs", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 0); SDL_GPUShader* upscale_frag = createShaderMSL(device_, UPSCALE_FRAG_MSL, "upscale_fs", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 0);
SDL_GPUShader* offscreen_frag = createShaderMSL(device_, POSTFX_FRAG_MSL, "postfx_fs", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 1);
SDL_GPUShader* downscale_frag = createShaderMSL(device_, DOWNSCALE_FRAG_MSL, "downscale_fs", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 1);
#else #else
SDL_GPUShader* uvert = 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);
SDL_GPUShader* ufrag = createShaderSPIRV(device_, kupscale_frag_spv, kupscale_frag_spv_size, "main", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 0); SDL_GPUShader* upscale_frag = createShaderSPIRV(device_, kupscale_frag_spv, kupscale_frag_spv_size, "main", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 0);
SDL_GPUShader* offscreen_frag = createShaderSPIRV(device_, kpostfx_frag_spv, kpostfx_frag_spv_size, "main", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 1);
SDL_GPUShader* downscale_frag = createShaderSPIRV(device_, kdownscale_frag_spv, kdownscale_frag_spv_size, "main", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 1);
#endif #endif
if ((uvert == nullptr) || (ufrag == nullptr)) { pipeline_ = createPostfxLikePipeline(postfx_frag, SWAPCHAIN_FMT, "PostFX");
SDL_Log("SDL3GPUShader: failed to compile upscale shaders"); upscale_pipeline_ = createPostfxLikePipeline(upscale_frag, OFFSCREEN_FMT, "upscale");
if (uvert != nullptr) { SDL_ReleaseGPUShader(device_, uvert); } postfx_offscreen_pipeline_ = createPostfxLikePipeline(offscreen_frag, OFFSCREEN_FMT, "PostFX offscreen");
if (ufrag != nullptr) { SDL_ReleaseGPUShader(device_, ufrag); } downscale_pipeline_ = createPostfxLikePipeline(downscale_frag, SWAPCHAIN_FMT, "downscale");
return false;
}
SDL_GPUColorTargetDescription upscale_color_target = {}; return (pipeline_ != nullptr) && (upscale_pipeline_ != nullptr) && (postfx_offscreen_pipeline_ != nullptr) && (downscale_pipeline_ != nullptr);
upscale_color_target.format = SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM;
upscale_color_target.blend_state = no_blend;
SDL_GPUGraphicsPipelineCreateInfo upscale_pipe_info = {};
upscale_pipe_info.vertex_shader = uvert;
upscale_pipe_info.fragment_shader = ufrag;
upscale_pipe_info.vertex_input_state = no_input;
upscale_pipe_info.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST;
upscale_pipe_info.target_info.num_color_targets = 1;
upscale_pipe_info.target_info.color_target_descriptions = &upscale_color_target;
upscale_pipeline_ = SDL_CreateGPUGraphicsPipeline(device_, &upscale_pipe_info);
SDL_ReleaseGPUShader(device_, uvert);
SDL_ReleaseGPUShader(device_, ufrag);
if (upscale_pipeline_ == nullptr) {
SDL_Log("SDL3GPUShader: upscale pipeline creation failed: %s", SDL_GetError());
return false;
}
// ---- PostFX offscreen pipeline (scaled_texture_ → postfx_texture_, B8G8R8A8) ----
// Mismos shaders que pipeline_ pero con formato de salida B8G8R8A8_UNORM para textura intermedia.
#ifdef __APPLE__
SDL_GPUShader* ofvert = createShaderMSL(device_, POSTFX_VERT_MSL, "postfx_vs", SDL_GPU_SHADERSTAGE_VERTEX, 0, 0);
SDL_GPUShader* offrag = createShaderMSL(device_, POSTFX_FRAG_MSL, "postfx_fs", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 1);
#else
SDL_GPUShader* ofvert = createShaderSPIRV(device_, kpostfx_vert_spv, kpostfx_vert_spv_size, "main", SDL_GPU_SHADERSTAGE_VERTEX, 0, 0);
SDL_GPUShader* offrag = createShaderSPIRV(device_, kpostfx_frag_spv, kpostfx_frag_spv_size, "main", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 1);
#endif
if ((ofvert == nullptr) || (offrag == nullptr)) {
SDL_Log("SDL3GPUShader: failed to compile PostFX offscreen shaders");
if (ofvert != nullptr) { SDL_ReleaseGPUShader(device_, ofvert); }
if (offrag != nullptr) { SDL_ReleaseGPUShader(device_, offrag); }
return false;
}
SDL_GPUColorTargetDescription offscreen_color_target = {};
offscreen_color_target.format = SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM;
offscreen_color_target.blend_state = no_blend;
SDL_GPUGraphicsPipelineCreateInfo offscreen_pipe_info = {};
offscreen_pipe_info.vertex_shader = ofvert;
offscreen_pipe_info.fragment_shader = offrag;
offscreen_pipe_info.vertex_input_state = no_input;
offscreen_pipe_info.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST;
offscreen_pipe_info.target_info.num_color_targets = 1;
offscreen_pipe_info.target_info.color_target_descriptions = &offscreen_color_target;
postfx_offscreen_pipeline_ = SDL_CreateGPUGraphicsPipeline(device_, &offscreen_pipe_info);
SDL_ReleaseGPUShader(device_, ofvert);
SDL_ReleaseGPUShader(device_, offrag);
if (postfx_offscreen_pipeline_ == nullptr) {
SDL_Log("SDL3GPUShader: PostFX offscreen pipeline creation failed: %s", SDL_GetError());
return false;
}
// ---- Downscale pipeline (postfx_texture_ → swapchain, Lanczos) ----
#ifdef __APPLE__
SDL_GPUShader* dvert = createShaderMSL(device_, POSTFX_VERT_MSL, "postfx_vs", SDL_GPU_SHADERSTAGE_VERTEX, 0, 0);
SDL_GPUShader* dfrag = createShaderMSL(device_, DOWNSCALE_FRAG_MSL, "downscale_fs", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 1);
#else
SDL_GPUShader* dvert = createShaderSPIRV(device_, kpostfx_vert_spv, kpostfx_vert_spv_size, "main", SDL_GPU_SHADERSTAGE_VERTEX, 0, 0);
SDL_GPUShader* dfrag = createShaderSPIRV(device_, kdownscale_frag_spv, kdownscale_frag_spv_size, "main", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 1);
#endif
if ((dvert == nullptr) || (dfrag == nullptr)) {
SDL_Log("SDL3GPUShader: failed to compile downscale shaders");
if (dvert != nullptr) { SDL_ReleaseGPUShader(device_, dvert); }
if (dfrag != nullptr) { SDL_ReleaseGPUShader(device_, dfrag); }
return false;
}
SDL_GPUColorTargetDescription downscale_color_target = {};
downscale_color_target.format = SWAPCHAIN_FMT;
downscale_color_target.blend_state = no_blend;
SDL_GPUGraphicsPipelineCreateInfo downscale_pipe_info = {};
downscale_pipe_info.vertex_shader = dvert;
downscale_pipe_info.fragment_shader = dfrag;
downscale_pipe_info.vertex_input_state = no_input;
downscale_pipe_info.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST;
downscale_pipe_info.target_info.num_color_targets = 1;
downscale_pipe_info.target_info.color_target_descriptions = &downscale_color_target;
downscale_pipeline_ = SDL_CreateGPUGraphicsPipeline(device_, &downscale_pipe_info);
SDL_ReleaseGPUShader(device_, dvert);
SDL_ReleaseGPUShader(device_, dfrag);
if (downscale_pipeline_ == nullptr) {
SDL_Log("SDL3GPUShader: downscale pipeline creation failed: %s", SDL_GetError());
return false;
}
return true;
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -700,51 +613,13 @@ namespace Rendering {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
auto SDL3GPUShader::createCrtPiPipeline() -> bool { auto SDL3GPUShader::createCrtPiPipeline() -> bool {
const SDL_GPUTextureFormat SWAPCHAIN_FMT = SDL_GetGPUSwapchainTextureFormat(device_, window_); const SDL_GPUTextureFormat SWAPCHAIN_FMT = SDL_GetGPUSwapchainTextureFormat(device_, window_);
#ifdef __APPLE__ #ifdef __APPLE__
SDL_GPUShader* vert = createShaderMSL(device_, POSTFX_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* frag = createShaderMSL(device_, CRTPI_FRAG_MSL, "crtpi_fs", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 1);
#else #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); SDL_GPUShader* frag = createShaderSPIRV(device_, kcrtpi_frag_spv, kcrtpi_frag_spv_size, "main", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 1);
#endif #endif
crtpi_pipeline_ = createPostfxLikePipeline(frag, SWAPCHAIN_FMT, "CrtPi");
if ((vert == nullptr) || (frag == nullptr)) { return crtpi_pipeline_ != nullptr;
SDL_Log("SDL3GPUShader: failed to compile CrtPi shaders");
if (vert != nullptr) { SDL_ReleaseGPUShader(device_, vert); }
if (frag != nullptr) { SDL_ReleaseGPUShader(device_, frag); }
return false;
}
SDL_GPUColorTargetBlendState no_blend = {};
no_blend.enable_blend = false;
no_blend.enable_color_write_mask = false;
SDL_GPUColorTargetDescription color_target = {};
color_target.format = SWAPCHAIN_FMT;
color_target.blend_state = no_blend;
SDL_GPUVertexInputState no_input = {};
SDL_GPUGraphicsPipelineCreateInfo pipe_info = {};
pipe_info.vertex_shader = vert;
pipe_info.fragment_shader = frag;
pipe_info.vertex_input_state = no_input;
pipe_info.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST;
pipe_info.target_info.num_color_targets = 1;
pipe_info.target_info.color_target_descriptions = &color_target;
crtpi_pipeline_ = SDL_CreateGPUGraphicsPipeline(device_, &pipe_info);
SDL_ReleaseGPUShader(device_, vert);
SDL_ReleaseGPUShader(device_, frag);
if (crtpi_pipeline_ == nullptr) {
SDL_Log("SDL3GPUShader: CrtPi pipeline creation failed: %s", SDL_GetError());
return false;
}
return true;
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -768,87 +643,70 @@ namespace Rendering {
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// render — upload scene texture + PostFX pass → swapchain // maybeRescaleSsTexture — recalcula factor SS i recrea scaled_texture_ si cal
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
void SDL3GPUShader::render() { // NOLINT(readability-function-cognitive-complexity) void SDL3GPUShader::maybeRescaleSsTexture() {
if (!is_initialized_) { return; } if (oversample_ <= 1 || game_height_ <= 0) { return; }
int win_w = 0;
// Paso 0: si SS activo, calcular el factor necesario según el zoom actual y recrear si cambió. int win_h = 0;
// Factor = primer múltiplo de 3 >= zoom (mín 3). Se recrea solo en saltos de factor. SDL_GetWindowSizeInPixels(window_, &win_w, &win_h);
if (oversample_ > 1 && game_height_ > 0) { const float ZOOM = static_cast<float>(win_h) / static_cast<float>(game_height_);
int win_w = 0; const int NEED_FACTOR = calcSsFactor(ZOOM);
int win_h = 0; if (NEED_FACTOR != ss_factor_) {
SDL_GetWindowSizeInPixels(window_, &win_w, &win_h); SDL_WaitForGPUIdle(device_);
const float ZOOM = static_cast<float>(win_h) / static_cast<float>(game_height_); recreateScaledTexture(NEED_FACTOR);
const int NEED_FACTOR = calcSsFactor(ZOOM);
if (NEED_FACTOR != ss_factor_) {
SDL_WaitForGPUIdle(device_);
recreateScaledTexture(NEED_FACTOR);
}
} }
}
SDL_GPUCommandBuffer* cmd = SDL_AcquireGPUCommandBuffer(device_); // ---------------------------------------------------------------------------
if (cmd == nullptr) { // uploadSceneTexture — copy pass: transfer buffer → scene texture
SDL_Log("SDL3GPUShader: SDL_AcquireGPUCommandBuffer failed: %s", SDL_GetError()); // ---------------------------------------------------------------------------
return; void SDL3GPUShader::uploadSceneTexture(SDL_GPUCommandBuffer* cmd) {
}
// ---- Copy pass: transfer buffer → scene texture (siempre a resolución del juego) ----
SDL_GPUCopyPass* copy = SDL_BeginGPUCopyPass(cmd); SDL_GPUCopyPass* copy = SDL_BeginGPUCopyPass(cmd);
if (copy != nullptr) { if (copy == nullptr) { return; }
SDL_GPUTextureTransferInfo src = {};
src.transfer_buffer = upload_buffer_;
src.offset = 0;
src.pixels_per_row = static_cast<Uint32>(game_width_);
src.rows_per_layer = static_cast<Uint32>(game_height_);
SDL_GPUTextureRegion dst = {}; SDL_GPUTextureTransferInfo src = {};
dst.texture = scene_texture_; src.transfer_buffer = upload_buffer_;
dst.w = static_cast<Uint32>(game_width_); src.offset = 0;
dst.h = static_cast<Uint32>(game_height_); src.pixels_per_row = static_cast<Uint32>(game_width_);
dst.d = 1; src.rows_per_layer = static_cast<Uint32>(game_height_);
SDL_UploadToGPUTexture(copy, &src, &dst, false); SDL_GPUTextureRegion dst = {};
SDL_EndGPUCopyPass(copy); dst.texture = scene_texture_;
} dst.w = static_cast<Uint32>(game_width_);
dst.h = static_cast<Uint32>(game_height_);
dst.d = 1;
// ---- Upscale pass: scene_texture_ → scaled_texture_ (NEAREST o LINEAR según linear_upscale_) ---- SDL_UploadToGPUTexture(copy, &src, &dst, false);
if (oversample_ > 1 && scaled_texture_ != nullptr && upscale_pipeline_ != nullptr) { SDL_EndGPUCopyPass(copy);
SDL_GPUColorTargetInfo upscale_target = {}; }
upscale_target.texture = scaled_texture_;
upscale_target.load_op = SDL_GPU_LOADOP_DONT_CARE;
upscale_target.store_op = SDL_GPU_STOREOP_STORE;
SDL_GPURenderPass* upass = SDL_BeginGPURenderPass(cmd, &upscale_target, 1, nullptr); // ---------------------------------------------------------------------------
if (upass != nullptr) { // runUpscalePass — scene_texture_ → scaled_texture_ (NEAREST o LINEAR segons linear_upscale_)
SDL_BindGPUGraphicsPipeline(upass, upscale_pipeline_); // ---------------------------------------------------------------------------
SDL_GPUTextureSamplerBinding ubinding = {}; void SDL3GPUShader::runUpscalePass(SDL_GPUCommandBuffer* cmd) {
ubinding.texture = scene_texture_; if (oversample_ <= 1 || scaled_texture_ == nullptr || upscale_pipeline_ == nullptr) { return; }
ubinding.sampler = (linear_upscale_ && linear_sampler_ != nullptr) ? linear_sampler_ : sampler_;
SDL_BindGPUFragmentSamplers(upass, 0, &ubinding, 1);
SDL_DrawGPUPrimitives(upass, 3, 1, 0, 0);
SDL_EndGPURenderPass(upass);
}
}
// ---- Acquire swapchain texture ---- SDL_GPUColorTargetInfo target = {};
SDL_GPUTexture* swapchain = nullptr; target.texture = scaled_texture_;
Uint32 sw = 0; target.load_op = SDL_GPU_LOADOP_DONT_CARE;
Uint32 sh = 0; target.store_op = SDL_GPU_STOREOP_STORE;
if (!SDL_AcquireGPUSwapchainTexture(cmd, window_, &swapchain, &sw, &sh)) {
SDL_Log("SDL3GPUShader: SDL_AcquireGPUSwapchainTexture failed: %s", SDL_GetError());
SDL_SubmitGPUCommandBuffer(cmd);
return;
}
if (swapchain == nullptr) {
// Window minimized — skip frame
SDL_SubmitGPUCommandBuffer(cmd);
return;
}
// ---- Calcular viewport (dimensiones lógicas del canvas, no de textura GPU) ---- SDL_GPURenderPass* pass = SDL_BeginGPURenderPass(cmd, &target, 1, nullptr);
float vx = 0.0F; if (pass == nullptr) { return; }
float vy = 0.0F; SDL_BindGPUGraphicsPipeline(pass, upscale_pipeline_);
SDL_GPUTextureSamplerBinding binding = {};
binding.texture = scene_texture_;
binding.sampler = (linear_upscale_ && linear_sampler_ != nullptr) ? linear_sampler_ : sampler_;
SDL_BindGPUFragmentSamplers(pass, 0, &binding, 1);
SDL_DrawGPUPrimitives(pass, 3, 1, 0, 0);
SDL_EndGPURenderPass(pass);
}
// ---------------------------------------------------------------------------
// computeViewport — dimensions lògiques del canvas dins del swapchain (letterbox)
// ---------------------------------------------------------------------------
auto SDL3GPUShader::computeViewport(Uint32 sw, Uint32 sh) const -> Viewport {
float vw = 0.0F; float vw = 0.0F;
float vh = 0.0F; float vh = 0.0F;
if (integer_scale_) { if (integer_scale_) {
@@ -862,131 +720,172 @@ namespace Rendering {
vw = static_cast<float>(game_width_) * SCALE; vw = static_cast<float>(game_width_) * SCALE;
vh = static_cast<float>(game_height_) * SCALE; vh = static_cast<float>(game_height_) * SCALE;
} }
vx = std::floor((static_cast<float>(sw) - vw) * 0.5F); const float VX = std::floor((static_cast<float>(sw) - vw) * 0.5F);
vy = std::floor((static_cast<float>(sh) - vh) * 0.5F); const float VY = std::floor((static_cast<float>(sh) - vh) * 0.5F);
return {.x = VX, .y = VY, .w = vw, .h = vh};
}
// pixel_scale: subpíxeles por pixel lógico. // ---------------------------------------------------------------------------
// Sin SS: vh/game_height (zoom de ventana). // updateDynamicUniforms — actualitza pixel_scale, time, oversample per a aquest frame
// Con SS: ss_factor_ exacto (3, 6, 9...). // ---------------------------------------------------------------------------
void SDL3GPUShader::updateDynamicUniforms(float viewport_h) {
// pixel_scale: subpíxels per pixel lògic. Amb SS: ss_factor_ exacte; sense SS: zoom de finestra.
if (oversample_ > 1 && ss_factor_ > 0) { if (oversample_ > 1 && ss_factor_ > 0) {
uniforms_.pixel_scale = static_cast<float>(ss_factor_); uniforms_.pixel_scale = static_cast<float>(ss_factor_);
} else { } else {
uniforms_.pixel_scale = (game_height_ > 0) ? (vh / static_cast<float>(game_height_)) : 1.0F; uniforms_.pixel_scale = (game_height_ > 0) ? (viewport_h / static_cast<float>(game_height_)) : 1.0F;
} }
uniforms_.time = static_cast<float>(SDL_GetTicks()) / 1000.0F; uniforms_.time = static_cast<float>(SDL_GetTicks()) / 1000.0F;
uniforms_.oversample = (oversample_ > 1 && ss_factor_ > 0) uniforms_.oversample = (oversample_ > 1 && ss_factor_ > 0) ? static_cast<float>(ss_factor_) : 1.0F;
? static_cast<float>(ss_factor_) }
: 1.0F;
// ---- Path CrtPi: directo scene_texture_ → swapchain, sin SS ni Lanczos ---- // ---------------------------------------------------------------------------
if (active_shader_ == ShaderType::CRTPI && crtpi_pipeline_ != nullptr) { // runCrtPiPass — scene_texture_ → swapchain via pipeline CrtPi (sense SS ni Lanczos)
SDL_GPUColorTargetInfo color_target = {}; // ---------------------------------------------------------------------------
color_target.texture = swapchain; void SDL3GPUShader::runCrtPiPass(SDL_GPUCommandBuffer* cmd, SDL_GPUTexture* swapchain, const Viewport& vp) {
color_target.load_op = SDL_GPU_LOADOP_CLEAR; SDL_GPUColorTargetInfo color_target = {};
color_target.store_op = SDL_GPU_STOREOP_STORE; color_target.texture = swapchain;
color_target.clear_color = {.r = 0.0F, .g = 0.0F, .b = 0.0F, .a = 1.0F}; color_target.load_op = SDL_GPU_LOADOP_CLEAR;
color_target.store_op = SDL_GPU_STOREOP_STORE;
color_target.clear_color = {.r = 0.0F, .g = 0.0F, .b = 0.0F, .a = 1.0F};
SDL_GPURenderPass* pass = SDL_BeginGPURenderPass(cmd, &color_target, 1, nullptr); SDL_GPURenderPass* pass = SDL_BeginGPURenderPass(cmd, &color_target, 1, nullptr);
if (pass != nullptr) { if (pass == nullptr) { return; }
SDL_BindGPUGraphicsPipeline(pass, crtpi_pipeline_); SDL_BindGPUGraphicsPipeline(pass, crtpi_pipeline_);
SDL_GPUViewport vp = {.x = vx, .y = vy, .w = vw, .h = vh, .min_depth = 0.0F, .max_depth = 1.0F}; SDL_GPUViewport sdlvp = {.x = vp.x, .y = vp.y, .w = vp.w, .h = vp.h, .min_depth = 0.0F, .max_depth = 1.0F};
SDL_SetGPUViewport(pass, &vp); SDL_SetGPUViewport(pass, &sdlvp);
SDL_GPUTextureSamplerBinding binding = {}; SDL_GPUTextureSamplerBinding binding = {};
binding.texture = scene_texture_; binding.texture = scene_texture_;
binding.sampler = sampler_; // NEAREST: el shader CrtPi hace su propio filtrado analítico binding.sampler = sampler_; // NEAREST: el shader CrtPi fa el seu filtrat analític
SDL_BindGPUFragmentSamplers(pass, 0, &binding, 1); SDL_BindGPUFragmentSamplers(pass, 0, &binding, 1);
// Inyectar texture_width/height antes del push crtpi_uniforms_.texture_width = static_cast<float>(game_width_);
crtpi_uniforms_.texture_width = static_cast<float>(game_width_); crtpi_uniforms_.texture_height = static_cast<float>(game_height_);
crtpi_uniforms_.texture_height = static_cast<float>(game_height_); SDL_PushGPUFragmentUniformData(cmd, 0, &crtpi_uniforms_, sizeof(CrtPiUniforms));
SDL_PushGPUFragmentUniformData(cmd, 0, &crtpi_uniforms_, sizeof(CrtPiUniforms));
SDL_DrawGPUPrimitives(pass, 3, 1, 0, 0); SDL_DrawGPUPrimitives(pass, 3, 1, 0, 0);
SDL_EndGPURenderPass(pass); SDL_EndGPURenderPass(pass);
} }
// ---------------------------------------------------------------------------
// runLanczosPasses — scaled_texture_ → postfx_texture_ (PostFX) → swapchain (Lanczos)
// ---------------------------------------------------------------------------
void SDL3GPUShader::runLanczosPasses(SDL_GPUCommandBuffer* cmd, SDL_GPUTexture* swapchain, const Viewport& vp) {
// Pass A: PostFX → postfx_texture_ (full scaled size, sense viewport)
SDL_GPUColorTargetInfo postfx_target = {};
postfx_target.texture = postfx_texture_;
postfx_target.load_op = SDL_GPU_LOADOP_CLEAR;
postfx_target.store_op = SDL_GPU_STOREOP_STORE;
postfx_target.clear_color = {.r = 0.0F, .g = 0.0F, .b = 0.0F, .a = 1.0F};
SDL_GPURenderPass* ppass = SDL_BeginGPURenderPass(cmd, &postfx_target, 1, nullptr);
if (ppass != nullptr) {
SDL_BindGPUGraphicsPipeline(ppass, postfx_offscreen_pipeline_);
SDL_GPUTextureSamplerBinding pbinding = {};
pbinding.texture = scaled_texture_;
pbinding.sampler = sampler_; // NEAREST: 1:1 pass, efectes calculats analíticament
SDL_BindGPUFragmentSamplers(ppass, 0, &pbinding, 1);
SDL_PushGPUFragmentUniformData(cmd, 0, &uniforms_, sizeof(PostFXUniforms));
SDL_DrawGPUPrimitives(ppass, 3, 1, 0, 0);
SDL_EndGPURenderPass(ppass);
}
// Pass B: Downscale Lanczos → swapchain (amb viewport/letterbox)
SDL_GPUColorTargetInfo ds_target = {};
ds_target.texture = swapchain;
ds_target.load_op = SDL_GPU_LOADOP_CLEAR;
ds_target.store_op = SDL_GPU_STOREOP_STORE;
ds_target.clear_color = {.r = 0.0F, .g = 0.0F, .b = 0.0F, .a = 1.0F};
SDL_GPURenderPass* dpass = SDL_BeginGPURenderPass(cmd, &ds_target, 1, nullptr);
if (dpass == nullptr) { return; }
SDL_BindGPUGraphicsPipeline(dpass, downscale_pipeline_);
SDL_GPUViewport sdlvp = {.x = vp.x, .y = vp.y, .w = vp.w, .h = vp.h, .min_depth = 0.0F, .max_depth = 1.0F};
SDL_SetGPUViewport(dpass, &sdlvp);
SDL_GPUTextureSamplerBinding dbinding = {};
dbinding.texture = postfx_texture_;
dbinding.sampler = sampler_; // NEAREST: el shader Lanczos fa la seua pròpia interpolació
SDL_BindGPUFragmentSamplers(dpass, 0, &dbinding, 1);
// algorithm: 0=Lanczos2, 1=Lanczos3 (downscale_algo_ és 1-based)
DownscaleUniforms downscale_u = {.algorithm = downscale_algo_ - 1, .pad0 = 0.0F, .pad1 = 0.0F, .pad2 = 0.0F};
SDL_PushGPUFragmentUniformData(cmd, 0, &downscale_u, sizeof(DownscaleUniforms));
SDL_DrawGPUPrimitives(dpass, 3, 1, 0, 0);
SDL_EndGPURenderPass(dpass);
}
// ---------------------------------------------------------------------------
// runDirectPostfxPass — PostFX → swapchain directament (sense Lanczos)
// ---------------------------------------------------------------------------
void SDL3GPUShader::runDirectPostfxPass(SDL_GPUCommandBuffer* cmd, SDL_GPUTexture* swapchain, const Viewport& vp) {
SDL_GPUColorTargetInfo color_target = {};
color_target.texture = swapchain;
color_target.load_op = SDL_GPU_LOADOP_CLEAR;
color_target.store_op = SDL_GPU_STOREOP_STORE;
color_target.clear_color = {.r = 0.0F, .g = 0.0F, .b = 0.0F, .a = 1.0F};
SDL_GPURenderPass* pass = SDL_BeginGPURenderPass(cmd, &color_target, 1, nullptr);
if (pass == nullptr) { return; }
SDL_BindGPUGraphicsPipeline(pass, pipeline_);
SDL_GPUViewport sdlvp = {.x = vp.x, .y = vp.y, .w = vp.w, .h = vp.h, .min_depth = 0.0F, .max_depth = 1.0F};
SDL_SetGPUViewport(pass, &sdlvp);
// Amb SS: llegir de scaled_texture_ amb LINEAR; sense SS: scene_texture_ amb NEAREST.
SDL_GPUTexture* input_texture = (oversample_ > 1 && scaled_texture_ != nullptr) ? scaled_texture_ : scene_texture_;
SDL_GPUSampler* active_sampler = (oversample_ > 1 && linear_sampler_ != nullptr) ? linear_sampler_ : sampler_;
SDL_GPUTextureSamplerBinding binding = {};
binding.texture = input_texture;
binding.sampler = active_sampler;
SDL_BindGPUFragmentSamplers(pass, 0, &binding, 1);
SDL_PushGPUFragmentUniformData(cmd, 0, &uniforms_, sizeof(PostFXUniforms));
SDL_DrawGPUPrimitives(pass, 3, 1, 0, 0);
SDL_EndGPURenderPass(pass);
}
// ---------------------------------------------------------------------------
// render — orquestra upload + upscale + path PostFX (CrtPi / Lanczos / direct)
// ---------------------------------------------------------------------------
void SDL3GPUShader::render() {
if (!is_initialized_) { return; }
maybeRescaleSsTexture();
SDL_GPUCommandBuffer* cmd = SDL_AcquireGPUCommandBuffer(device_);
if (cmd == nullptr) {
SDL_Log("SDL3GPUShader: SDL_AcquireGPUCommandBuffer failed: %s", SDL_GetError());
return;
}
uploadSceneTexture(cmd);
runUpscalePass(cmd);
SDL_GPUTexture* swapchain = nullptr;
Uint32 sw = 0;
Uint32 sh = 0;
if (!SDL_AcquireGPUSwapchainTexture(cmd, window_, &swapchain, &sw, &sh)) {
SDL_Log("SDL3GPUShader: SDL_AcquireGPUSwapchainTexture failed: %s", SDL_GetError());
SDL_SubmitGPUCommandBuffer(cmd);
return;
}
if (swapchain == nullptr) {
// Finestra minimitzada — saltem el frame
SDL_SubmitGPUCommandBuffer(cmd); SDL_SubmitGPUCommandBuffer(cmd);
return; return;
} }
// ---- Determinar si usar el path Lanczos (SS activo + algo seleccionado) ---- const Viewport VP = computeViewport(sw, sh);
updateDynamicUniforms(VP.h);
const bool USE_LANCZOS = (oversample_ > 1 && downscale_algo_ > 0 && scaled_texture_ != nullptr && postfx_texture_ != nullptr && postfx_offscreen_pipeline_ != nullptr && downscale_pipeline_ != nullptr); const bool USE_LANCZOS = (oversample_ > 1 && downscale_algo_ > 0 && scaled_texture_ != nullptr && postfx_texture_ != nullptr && postfx_offscreen_pipeline_ != nullptr && downscale_pipeline_ != nullptr);
if (USE_LANCZOS) { if (active_shader_ == ShaderType::CRTPI && crtpi_pipeline_ != nullptr) {
// ---- Pass A: PostFX → postfx_texture_ (full scaled size, sin viewport) ---- runCrtPiPass(cmd, swapchain, VP);
SDL_GPUColorTargetInfo postfx_target = {}; } else if (USE_LANCZOS) {
postfx_target.texture = postfx_texture_; runLanczosPasses(cmd, swapchain, VP);
postfx_target.load_op = SDL_GPU_LOADOP_CLEAR;
postfx_target.store_op = SDL_GPU_STOREOP_STORE;
postfx_target.clear_color = {.r = 0.0F, .g = 0.0F, .b = 0.0F, .a = 1.0F};
SDL_GPURenderPass* ppass = SDL_BeginGPURenderPass(cmd, &postfx_target, 1, nullptr);
if (ppass != nullptr) {
SDL_BindGPUGraphicsPipeline(ppass, postfx_offscreen_pipeline_);
SDL_GPUTextureSamplerBinding pbinding = {};
pbinding.texture = scaled_texture_;
pbinding.sampler = sampler_; // NEAREST: 1:1 pass, efectos calculados analíticamente
SDL_BindGPUFragmentSamplers(ppass, 0, &pbinding, 1);
SDL_PushGPUFragmentUniformData(cmd, 0, &uniforms_, sizeof(PostFXUniforms));
SDL_DrawGPUPrimitives(ppass, 3, 1, 0, 0);
SDL_EndGPURenderPass(ppass);
}
// ---- Pass B: Downscale Lanczos → swapchain (con viewport/letterbox) ----
SDL_GPUColorTargetInfo ds_target = {};
ds_target.texture = swapchain;
ds_target.load_op = SDL_GPU_LOADOP_CLEAR;
ds_target.store_op = SDL_GPU_STOREOP_STORE;
ds_target.clear_color = {.r = 0.0F, .g = 0.0F, .b = 0.0F, .a = 1.0F};
SDL_GPURenderPass* dpass = SDL_BeginGPURenderPass(cmd, &ds_target, 1, nullptr);
if (dpass != nullptr) {
SDL_BindGPUGraphicsPipeline(dpass, downscale_pipeline_);
SDL_GPUViewport vp = {.x = vx, .y = vy, .w = vw, .h = vh, .min_depth = 0.0F, .max_depth = 1.0F};
SDL_SetGPUViewport(dpass, &vp);
SDL_GPUTextureSamplerBinding dbinding = {};
dbinding.texture = postfx_texture_;
dbinding.sampler = sampler_; // NEAREST: el shader Lanczos hace su propia interpolación
SDL_BindGPUFragmentSamplers(dpass, 0, &dbinding, 1);
// algorithm: 0=Lanczos2, 1=Lanczos3 (downscale_algo_ es 1-based)
DownscaleUniforms downscale_u = {.algorithm = downscale_algo_ - 1, .pad0 = 0.0F, .pad1 = 0.0F, .pad2 = 0.0F};
SDL_PushGPUFragmentUniformData(cmd, 0, &downscale_u, sizeof(DownscaleUniforms));
SDL_DrawGPUPrimitives(dpass, 3, 1, 0, 0);
SDL_EndGPURenderPass(dpass);
}
} else { } else {
// ---- Render pass: PostFX → swapchain directamente (bilinear, comportamiento original) ---- runDirectPostfxPass(cmd, swapchain, VP);
SDL_GPUColorTargetInfo color_target = {};
color_target.texture = swapchain;
color_target.load_op = SDL_GPU_LOADOP_CLEAR;
color_target.store_op = SDL_GPU_STOREOP_STORE;
color_target.clear_color = {.r = 0.0F, .g = 0.0F, .b = 0.0F, .a = 1.0F};
SDL_GPURenderPass* pass = SDL_BeginGPURenderPass(cmd, &color_target, 1, nullptr);
if (pass != nullptr) {
SDL_BindGPUGraphicsPipeline(pass, pipeline_);
SDL_GPUViewport vp = {.x = vx, .y = vy, .w = vw, .h = vh, .min_depth = 0.0F, .max_depth = 1.0F};
SDL_SetGPUViewport(pass, &vp);
// Con SS: leer de scaled_texture_ con LINEAR; sin SS: scene_texture_ con NEAREST.
SDL_GPUTexture* input_texture = (oversample_ > 1 && scaled_texture_ != nullptr)
? scaled_texture_
: scene_texture_;
SDL_GPUSampler* active_sampler = (oversample_ > 1 && linear_sampler_ != nullptr)
? linear_sampler_
: sampler_;
SDL_GPUTextureSamplerBinding binding = {};
binding.texture = input_texture;
binding.sampler = active_sampler;
SDL_BindGPUFragmentSamplers(pass, 0, &binding, 1);
SDL_PushGPUFragmentUniformData(cmd, 0, &uniforms_, sizeof(PostFXUniforms));
SDL_DrawGPUPrimitives(pass, 3, 1, 0, 0);
SDL_EndGPURenderPass(pass);
}
} }
SDL_SubmitGPUCommandBuffer(cmd); SDL_SubmitGPUCommandBuffer(cmd);
@@ -1090,7 +989,7 @@ namespace Rendering {
return shader; return shader;
} }
auto SDL3GPUShader::createShaderSPIRV(SDL_GPUDevice* device, // NOLINT(readability-convert-member-functions-to-static) auto SDL3GPUShader::createShaderSPIRV(SDL_GPUDevice* device,
const uint8_t* spv_code, const uint8_t* spv_code,
size_t spv_size, size_t spv_size,
const char* entrypoint, const char* entrypoint,
@@ -137,7 +137,24 @@ namespace Rendering {
Uint32 num_uniform_buffers) -> SDL_GPUShader*; Uint32 num_uniform_buffers) -> SDL_GPUShader*;
auto createPipeline() -> bool; auto createPipeline() -> bool;
auto createCrtPiPipeline() -> bool; // Pipeline dedicado para el shader CrtPi auto createCrtPiPipeline() -> bool; // Pipeline dedicado para el shader CrtPi
auto createPostfxVertexShader() -> SDL_GPUShader*; // Vertex shader fullscreen-triangle compartido (MSL/SPIRV)
// Empaqueta el patrón vert(postfx) + frag dado + target format en un pipeline gráfico.
// Toma ownership de `frag`: lo libera tras crear el pipeline (o si vert falla).
auto createPostfxLikePipeline(SDL_GPUShader* frag, SDL_GPUTextureFormat format, const char* debug_name) -> SDL_GPUGraphicsPipeline*;
// Sub-passos de render() (extrets per reduir complexitat ciclomàtica)
struct Viewport {
float x, y, w, h;
};
void maybeRescaleSsTexture();
void uploadSceneTexture(SDL_GPUCommandBuffer* cmd);
void runUpscalePass(SDL_GPUCommandBuffer* cmd);
[[nodiscard]] auto computeViewport(Uint32 sw, Uint32 sh) const -> Viewport;
void updateDynamicUniforms(float viewport_h);
void runCrtPiPass(SDL_GPUCommandBuffer* cmd, SDL_GPUTexture* swapchain, const Viewport& vp);
void runLanczosPasses(SDL_GPUCommandBuffer* cmd, SDL_GPUTexture* swapchain, const Viewport& vp);
void runDirectPostfxPass(SDL_GPUCommandBuffer* cmd, SDL_GPUTexture* swapchain, const Viewport& vp);
auto reinitTexturesAndBuffer() -> bool; // Recrea scene_texture_ y upload_buffer_ auto reinitTexturesAndBuffer() -> bool; // Recrea scene_texture_ y upload_buffer_
auto recreateScaledTexture(int factor) -> bool; // Recrea scaled_texture_ para factor dado auto recreateScaledTexture(int factor) -> bool; // Recrea scaled_texture_ para factor dado
static auto calcSsFactor(float zoom) -> int; // Primer múltiplo de 3 >= zoom (mín 3) static auto calcSsFactor(float zoom) -> int; // Primer múltiplo de 3 >= zoom (mín 3)
+1 -3
View File
@@ -27,9 +27,7 @@ void SmartSprite::init() {
// Actualiza la posición y comprueba si ha llegado a su destino // Actualiza la posición y comprueba si ha llegado a su destino
void SmartSprite::update() { void SmartSprite::update() {
if (enabled_) { if (enabled_) {
// Actualiza només la posició; els SmartSprite no usen animació de // NOLINTNEXTLINE(bugprone-parent-virtual-call): salt deliberat a l'avi — SmartSprite hereta d'AnimatedSprite només per reutilitzar API, però no usa animació de frames, així que es salta AnimatedSprite::update() (que cridaria animate())
// frames i salten deliberadament AnimatedSprite::animate().
// NOLINTNEXTLINE(bugprone-parent-virtual-call)
MovingSprite::update(); MovingSprite::update();
// Comprueba el movimiento // Comprueba el movimiento
+9 -14
View File
@@ -157,21 +157,16 @@ auto ResourcePack::addDirectory(const std::string& directory) -> bool {
return false; return false;
} }
// cppcheck-suppress useStlAlgorithm auto iter = std::filesystem::recursive_directory_iterator(directory);
for (const auto& entry : std::filesystem::recursive_directory_iterator(directory)) { return std::all_of(begin(iter), end(iter), [&](const std::filesystem::directory_entry& entry) {
if (entry.is_regular_file()) { if (!entry.is_regular_file()) {
std::string filepath = entry.path().string(); return true;
std::string filename = std::filesystem::relative(entry.path(), directory).string();
std::ranges::replace(filename, '\\', '/');
if (!addFile(filename, filepath)) {
return false;
}
} }
} std::string filepath = entry.path().string();
std::string filename = std::filesystem::relative(entry.path(), directory).string();
return true; std::ranges::replace(filename, '\\', '/');
return addFile(filename, filepath);
});
} }
auto ResourcePack::getResource(const std::string& filename) -> std::vector<uint8_t> { auto ResourcePack::getResource(const std::string& filename) -> std::vector<uint8_t> {
+2 -2
View File
@@ -870,13 +870,13 @@ void Title::iterate() {
demo_game_->iterate(); demo_game_->iterate();
if (demo_game_->hasFinished()) { if (demo_game_->hasFinished()) {
// cppcheck-suppress knownConditionTrueFalse // cppcheck-suppress knownConditionTrueFalse ; fals positiu: iterate() pot escriure section_->name=SECTION_PROG_QUIT (Alt+F4), cppcheck no creua la crida
const bool WAS_QUIT = (section_->name == SECTION_PROG_QUIT); const bool WAS_QUIT = (section_->name == SECTION_PROG_QUIT);
delete demo_game_; delete demo_game_;
demo_game_ = nullptr; demo_game_ = nullptr;
demo_game_active_ = false; demo_game_active_ = false;
// cppcheck-suppress knownConditionTrueFalse // cppcheck-suppress knownConditionTrueFalse ; fals positiu: WAS_QUIT depèn de iterate() que pot mutar section_->name
if (WAS_QUIT) { if (WAS_QUIT) {
section_->name = SECTION_PROG_QUIT; section_->name = SECTION_PROG_QUIT;
} else if (demo_then_instructions_) { } else if (demo_then_instructions_) {