feat(render): resolució d'offscreen configurable via YAML

Separa el tamany lògic (1280×720) del render target offscreen. Llista
tancada de 5 presets 16:9 (720p/900p/1080p/1440p/2160p) llegida de
rendering.render_{width,height} amb fallback a 1280×720 si invàlida.
Inclou API resizeRenderTarget() preparada per al menú de servei futur.
This commit is contained in:
2026-05-21 08:46:22 +02:00
parent 4252f3327f
commit 5d1dae1d86
6 changed files with 150 additions and 17 deletions
@@ -14,9 +14,11 @@ namespace Rendering::GPU {
GpuFrameRenderer::~GpuFrameRenderer() { destroy(); }
auto GpuFrameRenderer::init(SDL_Window* window, float logical_w, float logical_h) -> bool {
auto GpuFrameRenderer::init(SDL_Window* window, float logical_w, float logical_h, float render_w, float render_h) -> bool {
logical_w_ = logical_w;
logical_h_ = logical_h;
render_w_ = render_w;
render_h_ = render_h;
if (!device_.init(window)) {
return false;
@@ -47,13 +49,15 @@ namespace Rendering::GPU {
return false;
}
// Textura offscreen del tamaño lógico del juego, COLOR_TARGET + SAMPLER.
// Textura offscreen del tamaño físico de render, COLOR_TARGET + SAMPLER.
// El tamaño lógico se aplica a los vértices vía UBO; el offscreen puede
// ser de mayor resolución para ganar nitidez tras el upscale a la swapchain.
SDL_GPUTextureCreateInfo tex_info{};
tex_info.type = SDL_GPU_TEXTURETYPE_2D;
tex_info.format = offscreen_format_;
tex_info.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET | SDL_GPU_TEXTUREUSAGE_SAMPLER;
tex_info.width = static_cast<uint32_t>(logical_w_);
tex_info.height = static_cast<uint32_t>(logical_h_);
tex_info.width = static_cast<uint32_t>(render_w_);
tex_info.height = static_cast<uint32_t>(render_h_);
tex_info.layer_count_or_depth = 1;
tex_info.num_levels = 1;
tex_info.sample_count = SDL_GPU_SAMPLECOUNT_1;
@@ -107,6 +111,19 @@ namespace Rendering::GPU {
indices_.clear();
}
auto GpuFrameRenderer::resizeRenderTarget(float render_w, float render_h) -> bool {
// Solo seguro fuera de un frame: si el cmd buffer está vivo y referencia
// la textura antigua, recrearla provocaría un cuelgue/UB.
if (isInsideFrame()) {
std::cerr << "[GpuFrameRenderer] resizeRenderTarget llamado dentro de frame, ignorado\n";
return false;
}
destroyOffscreen();
render_w_ = render_w;
render_h_ = render_h;
return createOffscreen();
}
auto GpuFrameRenderer::beginFrame(float clear_r, float clear_g, float clear_b) -> bool {
// Los clear_* se ignoran: el fondo lo pinta el postpro. Mantenemos la
// firma para no romper el SDLManager.
@@ -438,8 +455,9 @@ namespace Rendering::GPU {
ubo.background_max_g = BG_MAX_G;
ubo.background_max_b = BG_MAX_B;
ubo.background_max_a = 1.0F;
ubo.texel_size_x = 1.0F / logical_w_;
ubo.texel_size_y = 1.0F / logical_h_;
// El sampling del bloom muestrea el offscreen → texel size del tamaño físico.
ubo.texel_size_x = 1.0F / render_w_;
ubo.texel_size_y = 1.0F / render_h_;
ubo.pad_b = 0.0F;
ubo.pad_c = 0.0F;
@@ -59,12 +59,24 @@ namespace Rendering::GPU {
GpuFrameRenderer(GpuFrameRenderer&&) = delete;
auto operator=(GpuFrameRenderer&&) -> GpuFrameRenderer& = delete;
// Crea device + pipeline + offscreen + sampler. logical_w/h = tamaño
// en píxeles lógicos del juego (1280×720), usado como base del
// offscreen y de la transformación a NDC del shader de líneas.
[[nodiscard]] auto init(SDL_Window* window, float logical_w, float logical_h) -> bool;
// Crea device + pipeline + offscreen + sampler.
// logical_w/h: tamaño en píxeles lógicos del juego (1280×720). Lo
// consume el shader de líneas para transformar a NDC.
// render_w/h: tamaño físico del offscreen donde se rasterizan las
// líneas. Puede ser > logical para ganar nitidez al
// escalar a la swapchain (configurable vía YAML).
[[nodiscard]] auto init(SDL_Window* window,
float logical_w,
float logical_h,
float render_w,
float render_h) -> bool;
void destroy();
// Recrea el offscreen con un nuevo tamaño físico de render. Solo es
// seguro fuera de un frame (isInsideFrame() == false). Devuelve false
// si está dentro de frame o si la creación de la textura falla.
[[nodiscard]] auto resizeRenderTarget(float render_w, float render_h) -> bool;
// beginFrame: adquiere swapchain, abre render pass sobre offscreen
// con clear a negro. Devuelve false si no hay textura disponible.
// Los argumentos clear_r/g/b se ignoran (compatibilidad de API: el
@@ -114,10 +126,18 @@ namespace Rendering::GPU {
GpuLinePipeline line_pipeline_;
GpuPostFxPipeline postfx_pipeline_;
// Tamaño lógico del juego (= tamaño del offscreen).
// Tamaño lógico del juego: espacio de coordenadas de las primitivas
// (vértices, UBO del line shader). Fijo a 1280×720.
float logical_w_{1280.0F};
float logical_h_{720.0F};
// Tamaño físico del offscreen (configurable). Independiente del lógico:
// las coordenadas en NDC son agnósticas a la resolución de salida, así
// que rasterizar a mayor render_w_/h_ da líneas más nítidas tras el
// upscale lineal a la swapchain.
float render_w_{1280.0F};
float render_h_{720.0F};
// Viewport del pase final en píxeles físicos. <0 = full window.
float viewport_x_{0.0F};
float viewport_y_{0.0F};