primera implementacio de sdl3gpu

This commit is contained in:
2026-04-04 19:49:58 +02:00
parent 2c7b70911e
commit 2a774f777f
10 changed files with 103 additions and 29 deletions

View File

@@ -812,23 +812,40 @@ namespace Rendering {
SDL_EndGPUCopyPass(copy);
}
// ---- Upscale pass: scene_texture_ → scaled_texture_ (NEAREST o LINEAR según linear_upscale_) ----
// ---- Upscale pass: scene_texture_ → scaled_texture_ ----
// Si 4:3 actiu, l'estirament s'aplica ací directament (320x200 → W*factor × H*factor*1.2)
// El filtre per al 4:3 és configurable (stretch_filter_linear_).
// L'effective_scene/height reflecteix la textura real que veuen els shaders.
// Sense SS ni stretch: scene_texture_ a game_height_.
// Amb SS o stretch: scaled_texture_ a l'alçada escalada (amb o sense 4:3).
SDL_GPUTexture* effective_scene = scene_texture_;
int effective_height = game_height_;
if (oversample_ > 1 && scaled_texture_ != nullptr && upscale_pipeline_ != nullptr) {
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;
// Triar filtre: si 4:3 actiu, usar el filtre configurable per a l'estirament.
// Si no, usar el filtre d'upscale normal (linear_upscale_).
bool use_linear = stretch_4_3_ ? stretch_filter_linear_ : linear_upscale_;
SDL_GPURenderPass* upass = SDL_BeginGPURenderPass(cmd, &upscale_target, 1, nullptr);
if (upass != nullptr) {
SDL_BindGPUGraphicsPipeline(upass, upscale_pipeline_);
SDL_GPUTextureSamplerBinding ubinding = {};
ubinding.texture = scene_texture_;
ubinding.sampler = (linear_upscale_ && linear_sampler_ != nullptr) ? linear_sampler_ : sampler_;
ubinding.sampler = (use_linear && linear_sampler_ != nullptr) ? linear_sampler_ : sampler_;
SDL_BindGPUFragmentSamplers(upass, 0, &ubinding, 1);
SDL_DrawGPUPrimitives(upass, 3, 1, 0, 0);
SDL_EndGPURenderPass(upass);
}
effective_scene = scaled_texture_;
effective_height = stretch_4_3_ ? static_cast<int>(static_cast<float>(game_height_) * 1.2F) : game_height_;
} else if (stretch_4_3_) {
// Sense SS: el viewport s'encarrega de l'estirament geomètric
effective_height = static_cast<int>(static_cast<float>(game_height_) * 1.2F);
}
// ---- Acquire swapchain texture ----
@@ -846,10 +863,10 @@ namespace Rendering {
return;
}
// ---- Calcular viewport (dimensions lògiques del canvas, no de textura GPU) ----
// Si stretch_4_3_ actiu, l'alçada lògica es multiplica per 1.2 (200→240) per simular 4:3 CRT
// ---- Calcular viewport (dimensions lògiques del canvas) ----
// Si 4:3 actiu, effective_height ja és 240 (la textura estirada)
const float logical_w = static_cast<float>(game_width_);
const float logical_h = stretch_4_3_ ? static_cast<float>(game_height_) * 1.2F : static_cast<float>(game_height_);
const float logical_h = static_cast<float>(effective_height);
float vx = 0.0F;
float vy = 0.0F;
@@ -869,14 +886,15 @@ namespace Rendering {
vx = std::floor((static_cast<float>(sw) - vw) * 0.5F);
vy = std::floor((static_cast<float>(sh) - vh) * 0.5F);
// pixel_scale: subpíxeles por pixel lógico.
// Sin SS: vh/game_height (zoom de ventana).
// Con SS: ss_factor_ exacto (3, 6, 9...).
// pixel_scale: subpíxels per píxel lògic.
// Sense SS: vh/effective_height (zoom de finestra).
// Amb SS: ss_factor_ exacte (3, 6, 9...).
if (oversample_ > 1 && ss_factor_ > 0) {
uniforms_.pixel_scale = static_cast<float>(ss_factor_);
} else {
uniforms_.pixel_scale = (game_height_ > 0) ? (vh / static_cast<float>(game_height_)) : 1.0F;
uniforms_.pixel_scale = (effective_height > 0) ? (vh / static_cast<float>(effective_height)) : 1.0F;
}
uniforms_.screen_height = static_cast<float>(effective_height);
uniforms_.time = static_cast<float>(SDL_GetTicks()) / 1000.0F;
uniforms_.oversample = (oversample_ > 1 && ss_factor_ > 0)
? static_cast<float>(ss_factor_)
@@ -897,13 +915,13 @@ namespace Rendering {
SDL_SetGPUViewport(pass, &vp);
SDL_GPUTextureSamplerBinding binding = {};
binding.texture = scene_texture_;
binding.sampler = sampler_; // NEAREST: el shader CrtPi hace su propio filtrado analítico
binding.texture = effective_scene;
binding.sampler = sampler_; // NEAREST: el shader CrtPi fa el seu propi filtrat analític
SDL_BindGPUFragmentSamplers(pass, 0, &binding, 1);
// Inyectar texture_width/height antes del push
// Injectar texture_width/height abans del push
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>(effective_height);
SDL_PushGPUFragmentUniformData(cmd, 0, &crtpi_uniforms_, sizeof(CrtPiUniforms));
SDL_DrawGPUPrimitives(pass, 3, 1, 0, 0);
@@ -973,10 +991,10 @@ namespace Rendering {
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.
// Amb SS: llegir de scaled_texture_ amb LINEAR; sense SS: effective_scene amb NEAREST.
SDL_GPUTexture* input_texture = (oversample_ > 1 && scaled_texture_ != nullptr)
? scaled_texture_
: scene_texture_;
: effective_scene;
SDL_GPUSampler* active_sampler = (oversample_ > 1 && linear_sampler_ != nullptr)
? linear_sampler_
: sampler_;
@@ -1179,6 +1197,17 @@ namespace Rendering {
integer_scale_ = integer_scale;
}
void SDL3GPUShader::setStretch4_3(bool enabled) {
stretch_4_3_ = enabled;
if (!is_initialized_ || device_ == nullptr) return;
// Recrear scaled_texture_ perquè tinga les dimensions correctes (amb o sense 4:3)
if (oversample_ > 1 && ss_factor_ > 0) {
SDL_WaitForGPUIdle(device_);
recreateScaledTexture(ss_factor_);
}
}
// ---------------------------------------------------------------------------
// setOversample — cambia el factor SS; recrea texturas si ya está inicializado
// ---------------------------------------------------------------------------
@@ -1292,7 +1321,10 @@ namespace Rendering {
ss_factor_ = 0;
const int W = game_width_ * factor;
const int H = game_height_ * factor;
// Si 4:3 actiu, l'alçada inclou l'estirament (200 * factor * 1.2)
const int H = stretch_4_3_
? static_cast<int>(static_cast<float>(game_height_) * 1.2F * static_cast<float>(factor))
: game_height_ * factor;
SDL_GPUTextureCreateInfo info = {};
info.type = SDL_GPU_TEXTURETYPE_2D;

View File

@@ -120,9 +120,10 @@ namespace Rendering {
// Devuelve el shader activo
[[nodiscard]] auto getActiveShader() const -> ShaderType override { return active_shader_; }
// Estirament vertical 4:3 (320x200 → 320x240 visual al viewport)
void setStretch4_3(bool enabled) override { stretch_4_3_ = enabled; }
// Estirament vertical 4:3 (fusionat amb l'upscale pass)
void setStretch4_3(bool enabled) override;
[[nodiscard]] auto isStretch4_3() const -> bool override { return stretch_4_3_; }
void setStretchFilter(bool linear) override { stretch_filter_linear_ = linear; }
private:
static auto createShaderMSL(SDL_GPUDevice* device,
@@ -155,9 +156,9 @@ namespace Rendering {
SDL_GPUGraphicsPipeline* postfx_offscreen_pipeline_ = nullptr; // PostFX → postfx_texture_ (B8G8R8A8, solo con Lanczos)
SDL_GPUGraphicsPipeline* upscale_pipeline_ = nullptr; // Upscale pass (solo con SS)
SDL_GPUGraphicsPipeline* downscale_pipeline_ = nullptr; // Lanczos downscale (solo con SS + algo > 0)
SDL_GPUTexture* scene_texture_ = nullptr; // Canvas del juego (game_width_ × game_height_)
SDL_GPUTexture* scaled_texture_ = nullptr; // Upscale target (game×factor), solo con SS
SDL_GPUTexture* postfx_texture_ = nullptr; // PostFX output a resolución escalada, solo con Lanczos
SDL_GPUTexture* scene_texture_ = nullptr; // Canvas del joc (game_width_ × game_height_)
SDL_GPUTexture* scaled_texture_ = nullptr; // Upscale target (game×factor, amb 4:3 si actiu)
SDL_GPUTexture* postfx_texture_ = nullptr; // PostFX output a resolució escalada, sols amb Lanczos
SDL_GPUTransferBuffer* upload_buffer_ = nullptr;
SDL_GPUSampler* sampler_ = nullptr; // NEAREST
SDL_GPUSampler* linear_sampler_ = nullptr; // LINEAR
@@ -166,7 +167,7 @@ namespace Rendering {
CrtPiUniforms crtpi_uniforms_{.scanline_weight = 6.0F, .scanline_gap_brightness = 0.12F, .bloom_factor = 3.5F, .input_gamma = 2.4F, .output_gamma = 2.2F, .mask_brightness = 0.80F, .curvature_x = 0.05F, .curvature_y = 0.10F, .mask_type = 2, .enable_scanlines = 1, .enable_multisample = 1, .enable_gamma = 1};
ShaderType active_shader_ = ShaderType::POSTFX; // Shader de post-procesado activo
int game_width_ = 0; // Dimensiones originales del canvas
int game_width_ = 0; // Dimensions originals del canvas
int game_height_ = 0;
int ss_factor_ = 0; // Factor SS activo (3, 6, 9...) o 0 si SS desactivado
int oversample_ = 1; // SS on/off (1 = off, >1 = on)
@@ -176,8 +177,9 @@ namespace Rendering {
bool is_initialized_ = false;
bool vsync_ = true;
bool integer_scale_ = false;
bool linear_upscale_ = false; // Upscale NEAREST (false) o LINEAR (true)
bool stretch_4_3_ = false; // Estirament vertical 4:3 al viewport
bool linear_upscale_ = false; // Upscale NEAREST (false) o LINEAR (true)
bool stretch_4_3_ = false; // Estirament vertical 4:3
bool stretch_filter_linear_ = false; // Filtre per a l'estirament 4:3 (false=NEAREST, true=LINEAR)
};
} // namespace Rendering