treballant en internal resolution
This commit is contained in:
@@ -176,6 +176,11 @@ namespace Menu {
|
||||
? Locale::get("menu.values.linear")
|
||||
: Locale::get("menu.values.nearest")); }, [](int dir) { Screen::get()->cycleTextureFilter(dir); }, nullptr, nullptr, nullptr});
|
||||
|
||||
p.items.push_back({Locale::get("menu.items.internal_resolution"), ItemKind::IntRange, [] {
|
||||
char buf[16];
|
||||
std::snprintf(buf, sizeof(buf), "%dX", Options::video.internal_resolution);
|
||||
return std::string(buf); }, [](int dir) { Screen::get()->changeInternalResolution(dir); }, nullptr, nullptr, nullptr});
|
||||
|
||||
// Bloc shaders: no disponible a WASM (NO_SHADERS, sense SDL3 GPU a WebGL2)
|
||||
#ifndef __EMSCRIPTEN__
|
||||
p.items.push_back({Locale::get("menu.items.shader"), ItemKind::Toggle, [] { return onOff(Options::video.shader_enabled); }, [](int) { Screen::get()->toggleShaders(); }, nullptr, nullptr, nullptr});
|
||||
|
||||
@@ -37,6 +37,13 @@ Screen::Screen() {
|
||||
if (zoom_ < 1) zoom_ = 1;
|
||||
if (zoom_ > max_zoom_) zoom_ = max_zoom_;
|
||||
|
||||
// Clamp de la resolució interna a [1, max_zoom_]. Llegir del YAML i
|
||||
// ajustar aquí és l'únic moment en què es fa — el menú re-clampa cada
|
||||
// canvi. Si la pantalla és més petita que el valor desat (p.ex. canvi
|
||||
// de monitor), baixem al màxim suportat.
|
||||
if (Options::video.internal_resolution < 1) Options::video.internal_resolution = 1;
|
||||
if (Options::video.internal_resolution > max_zoom_) Options::video.internal_resolution = max_zoom_;
|
||||
|
||||
int w = GAME_WIDTH * zoom_;
|
||||
int h = Options::video.aspect_ratio_4_3 ? static_cast<int>(GAME_HEIGHT * 1.2F) * zoom_ : GAME_HEIGHT * zoom_;
|
||||
|
||||
@@ -66,6 +73,7 @@ Screen::~Screen() {
|
||||
shader_backend_.reset();
|
||||
}
|
||||
|
||||
if (internal_texture_sdl_) SDL_DestroyTexture(internal_texture_sdl_);
|
||||
if (texture_) SDL_DestroyTexture(texture_);
|
||||
if (renderer_) SDL_DestroyRenderer(renderer_);
|
||||
if (window_) SDL_DestroyWindow(window_);
|
||||
@@ -108,6 +116,8 @@ void Screen::initShaders() {
|
||||
shader_backend_->setOversample(3);
|
||||
}
|
||||
|
||||
shader_backend_->setInternalResolution(Options::video.internal_resolution);
|
||||
|
||||
// Resol el shader actiu des del config
|
||||
if (Options::video.current_shader == "crtpi") {
|
||||
shader_backend_->setActiveShader(Rendering::ShaderType::CRTPI);
|
||||
@@ -164,6 +174,33 @@ void Screen::present(Uint32* pixel_data) {
|
||||
} else {
|
||||
// Fallback SDL_Renderer
|
||||
SDL_UpdateTexture(texture_, nullptr, pixel_data, GAME_WIDTH * sizeof(Uint32));
|
||||
|
||||
const int mult = Options::video.internal_resolution;
|
||||
if (mult > 1) {
|
||||
// Resolució interna: còpia NN de texture_ → internal_texture_sdl_
|
||||
// (la fa SDL/GPU, no CPU). Tota la presentació downstream llegirà
|
||||
// d'aquesta textura més gran — el filtre LINEAR final parteix d'una
|
||||
// font més fina i l'estirament a finestra queda menys difús.
|
||||
ensureFallbackInternalTexture();
|
||||
if (internal_texture_sdl_ != nullptr) {
|
||||
SDL_SetTextureScaleMode(texture_, SDL_SCALEMODE_NEAREST);
|
||||
SDL_SetRenderTarget(renderer_, internal_texture_sdl_);
|
||||
SDL_RenderClear(renderer_);
|
||||
SDL_RenderTexture(renderer_, texture_, nullptr, nullptr);
|
||||
SDL_SetRenderTarget(renderer_, nullptr);
|
||||
// El filtre global (LINEAR/NEAREST) s'aplica a l'estirament final
|
||||
// cap a la finestra; per això l'aplicam a la textura intermèdia.
|
||||
SDL_ScaleMode final_scale = (Options::video.texture_filter == Options::TextureFilter::LINEAR)
|
||||
? SDL_SCALEMODE_LINEAR
|
||||
: SDL_SCALEMODE_NEAREST;
|
||||
SDL_SetTextureScaleMode(internal_texture_sdl_, final_scale);
|
||||
SDL_RenderClear(renderer_);
|
||||
SDL_RenderTexture(renderer_, internal_texture_sdl_, nullptr, nullptr);
|
||||
SDL_RenderPresent(renderer_);
|
||||
return;
|
||||
}
|
||||
// Si la creació de la textura ha fallat, caiem al path normal.
|
||||
}
|
||||
SDL_RenderClear(renderer_);
|
||||
SDL_RenderTexture(renderer_, texture_, nullptr, nullptr);
|
||||
SDL_RenderPresent(renderer_);
|
||||
@@ -261,6 +298,22 @@ void Screen::cycleTextureFilter(int dir) {
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::changeInternalResolution(int dir) {
|
||||
int next = Options::video.internal_resolution + (dir >= 0 ? 1 : -1);
|
||||
if (next < 1) next = 1;
|
||||
if (next > max_zoom_) next = max_zoom_;
|
||||
if (next == Options::video.internal_resolution) return;
|
||||
Options::video.internal_resolution = next;
|
||||
|
||||
// Propaga al backend actiu. Al fallback path, la textura es recrea al
|
||||
// pròxim present via ensureFallbackInternalTexture.
|
||||
if (shader_backend_) {
|
||||
shader_backend_->setInternalResolution(next);
|
||||
} else {
|
||||
applyFallbackPresentation();
|
||||
}
|
||||
}
|
||||
|
||||
auto Screen::nextShaderType() -> bool {
|
||||
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) return false;
|
||||
if (!Options::video.shader_enabled) return false;
|
||||
@@ -442,7 +495,41 @@ void Screen::applyFallbackPresentation() {
|
||||
case Options::ScalingMode::INTEGER: mode = SDL_LOGICAL_PRESENTATION_INTEGER_SCALE; break;
|
||||
}
|
||||
}
|
||||
SDL_SetRenderLogicalPresentation(renderer_, GAME_WIDTH, GAME_HEIGHT, mode);
|
||||
// Amb resolució interna N > 1, la mida lògica creix proporcionalment
|
||||
// perquè SDL scale des de 320·N × 200·N a la finestra — menys aggressive linear.
|
||||
const int mult = Options::video.internal_resolution < 1 ? 1 : Options::video.internal_resolution;
|
||||
SDL_SetRenderLogicalPresentation(renderer_, GAME_WIDTH * mult, GAME_HEIGHT * mult, mode);
|
||||
}
|
||||
|
||||
void Screen::ensureFallbackInternalTexture() {
|
||||
const int mult = Options::video.internal_resolution;
|
||||
if (mult <= 1 || renderer_ == nullptr) {
|
||||
// No cal textura intermèdia. Si la teníem, la reciclem.
|
||||
if (internal_texture_sdl_ != nullptr) {
|
||||
SDL_DestroyTexture(internal_texture_sdl_);
|
||||
internal_texture_sdl_ = nullptr;
|
||||
internal_texture_mult_ = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (internal_texture_sdl_ != nullptr && internal_texture_mult_ == mult) return;
|
||||
|
||||
if (internal_texture_sdl_ != nullptr) {
|
||||
SDL_DestroyTexture(internal_texture_sdl_);
|
||||
internal_texture_sdl_ = nullptr;
|
||||
}
|
||||
internal_texture_sdl_ = SDL_CreateTexture(renderer_,
|
||||
SDL_PIXELFORMAT_ABGR8888,
|
||||
SDL_TEXTUREACCESS_TARGET,
|
||||
GAME_WIDTH * mult,
|
||||
GAME_HEIGHT * mult);
|
||||
if (internal_texture_sdl_ == nullptr) {
|
||||
std::cerr << "Screen: failed to create fallback internal texture (×" << mult << "): "
|
||||
<< SDL_GetError() << '\n';
|
||||
internal_texture_mult_ = 0;
|
||||
return;
|
||||
}
|
||||
internal_texture_mult_ = mult;
|
||||
}
|
||||
|
||||
void Screen::adjustWindowSize() {
|
||||
|
||||
@@ -33,6 +33,7 @@ class Screen {
|
||||
void cycleScalingMode(int dir); // Cicla DISABLED/STRETCH/LETTERBOX/OVERSCAN/INTEGER
|
||||
void toggleVSync();
|
||||
void cycleTextureFilter(int dir); // Cicla NEAREST/LINEAR
|
||||
void changeInternalResolution(int dir); // +/−1, clampat a [1, max_zoom_]
|
||||
auto nextShaderType() -> bool; // false si GPU off / shaders off
|
||||
auto prevShaderType() -> bool; // idem
|
||||
auto nextPreset() -> bool; // false si GPU off / shaders off
|
||||
@@ -45,6 +46,7 @@ class Screen {
|
||||
// Getters
|
||||
[[nodiscard]] auto isFullscreen() const -> bool { return fullscreen_; }
|
||||
[[nodiscard]] auto getZoom() const -> int { return zoom_; }
|
||||
[[nodiscard]] auto getMaxZoom() const -> int { return max_zoom_; }
|
||||
[[nodiscard]] auto isHardwareAccelerated() const -> bool;
|
||||
[[nodiscard]] auto getActiveShaderName() const -> const char*;
|
||||
[[nodiscard]] auto getWindow() -> SDL_Window* { return window_; }
|
||||
@@ -58,12 +60,15 @@ class Screen {
|
||||
void calculateMaxZoom();
|
||||
void initShaders();
|
||||
void applyFallbackPresentation(); // Logical presentation + scale mode per al path SDL_Renderer
|
||||
void ensureFallbackInternalTexture(); // Recrea internal_texture_sdl_ si cal (fallback path)
|
||||
|
||||
static Screen* instance_;
|
||||
|
||||
SDL_Window* window_{nullptr};
|
||||
SDL_Renderer* renderer_{nullptr};
|
||||
SDL_Texture* texture_{nullptr}; // 320x200 streaming, ABGR8888 (fallback SDL_Renderer)
|
||||
SDL_Texture* texture_{nullptr}; // 320x200 streaming, ABGR8888 (fallback SDL_Renderer)
|
||||
SDL_Texture* internal_texture_sdl_{nullptr}; // 320·N x 200·N TARGET (fallback path, només si N>1)
|
||||
int internal_texture_mult_{0}; // Multiplicador amb què es va crear internal_texture_sdl_
|
||||
|
||||
// Backend GPU (nullptr si no disponible o desactivat)
|
||||
std::unique_ptr<Rendering::ShaderBackend> shader_backend_;
|
||||
|
||||
@@ -456,6 +456,11 @@ namespace Rendering {
|
||||
return false;
|
||||
}
|
||||
|
||||
// internal_texture_: si el multiplicador és > 1, es crea ací amb les
|
||||
// dimensions game·N × game·N. No bloqueja si falla — només deixa la
|
||||
// textura a nullptr i el pipeline ometrà la còpia.
|
||||
recreateInternalTexture();
|
||||
|
||||
// scaled_texture_ se creará en el primer render() una vez conocido el zoom de ventana
|
||||
ss_factor_ = 0;
|
||||
|
||||
@@ -812,14 +817,50 @@ namespace Rendering {
|
||||
SDL_EndGPUCopyPass(copy);
|
||||
}
|
||||
|
||||
// ---- Upscale pass: scene_texture_ → scaled_texture_ ----
|
||||
// ---- Internal resolution NN upscale: scene_texture_ → internal_texture_ ----
|
||||
// Multiplicador enter. Si > 1, tot el pipeline downstream veu internal_texture_
|
||||
// com a "scene" (mida game·N × game·N) i els passos següents (SS, PostFX,
|
||||
// Lanczos, letterbox) operen sobre aquesta font més gran. L'objectiu: quan el
|
||||
// filtre final LINEAR estira a finestra, parteix d'una base més gran i es veu
|
||||
// menys borrós. Amb internal_res_ == 1, s'omet el pas (zero overhead).
|
||||
SDL_GPUTexture* source_texture = scene_texture_;
|
||||
int source_width = game_width_;
|
||||
int source_height = game_height_;
|
||||
if (internal_res_ > 1 && internal_texture_ != nullptr && upscale_pipeline_ != nullptr) {
|
||||
SDL_GPUColorTargetInfo internal_target = {};
|
||||
internal_target.texture = internal_texture_;
|
||||
internal_target.load_op = SDL_GPU_LOADOP_DONT_CARE;
|
||||
internal_target.store_op = SDL_GPU_STOREOP_STORE;
|
||||
|
||||
SDL_GPURenderPass* ipass = SDL_BeginGPURenderPass(cmd, &internal_target, 1, nullptr);
|
||||
if (ipass != nullptr) {
|
||||
SDL_BindGPUGraphicsPipeline(ipass, upscale_pipeline_);
|
||||
SDL_GPUTextureSamplerBinding ibinding = {};
|
||||
ibinding.texture = scene_texture_;
|
||||
ibinding.sampler = sampler_; // sempre NEAREST per a la còpia de resolució interna
|
||||
SDL_BindGPUFragmentSamplers(ipass, 0, &ibinding, 1);
|
||||
SDL_DrawGPUPrimitives(ipass, 3, 1, 0, 0);
|
||||
SDL_EndGPURenderPass(ipass);
|
||||
}
|
||||
source_texture = internal_texture_;
|
||||
source_width = game_width_ * internal_res_;
|
||||
source_height = game_height_ * internal_res_;
|
||||
}
|
||||
|
||||
// ---- Upscale pass: source_texture → scaled_texture_ ----
|
||||
// Si 4:3 actiu, l'estirament s'aplica ací directament (320x200 → W*factor × H*factor*1.2)
|
||||
// El filtre s'aplica sempre (texture_filter_linear_), independent de 4:3.
|
||||
// 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_;
|
||||
SDL_GPUTexture* effective_scene = source_texture;
|
||||
// `effective_height` reflecteix l'alçada lògica del frame (per a
|
||||
// scanlines i viewport), no la mida real de la textura. Es manté
|
||||
// a `game_height_` encara que internal_res_ > 1 — el multiplicador
|
||||
// només afecta la resolució física de la font, no l'aspect ni el
|
||||
// nombre de scanlines visibles.
|
||||
int effective_height = game_height_;
|
||||
(void)source_width; // només es fa servir com a context informatiu
|
||||
|
||||
if (oversample_ > 1 && scaled_texture_ != nullptr && upscale_pipeline_ != nullptr) {
|
||||
SDL_GPUColorTargetInfo upscale_target = {};
|
||||
@@ -834,7 +875,7 @@ namespace Rendering {
|
||||
if (upass != nullptr) {
|
||||
SDL_BindGPUGraphicsPipeline(upass, upscale_pipeline_);
|
||||
SDL_GPUTextureSamplerBinding ubinding = {};
|
||||
ubinding.texture = scene_texture_;
|
||||
ubinding.texture = source_texture;
|
||||
ubinding.sampler = (use_linear && linear_sampler_ != nullptr) ? linear_sampler_ : sampler_;
|
||||
SDL_BindGPUFragmentSamplers(upass, 0, &ubinding, 1);
|
||||
SDL_DrawGPUPrimitives(upass, 3, 1, 0, 0);
|
||||
@@ -846,6 +887,7 @@ namespace Rendering {
|
||||
// Sense SS: el viewport s'encarrega de l'estirament geomètric
|
||||
effective_height = static_cast<int>(static_cast<float>(game_height_) * 1.2F);
|
||||
}
|
||||
(void)source_height;
|
||||
|
||||
// ---- Acquire swapchain texture ----
|
||||
SDL_GPUTexture* swapchain = nullptr;
|
||||
@@ -1068,6 +1110,10 @@ namespace Rendering {
|
||||
SDL_ReleaseGPUTexture(device_, scene_texture_);
|
||||
scene_texture_ = nullptr;
|
||||
}
|
||||
if (internal_texture_ != nullptr) {
|
||||
SDL_ReleaseGPUTexture(device_, internal_texture_);
|
||||
internal_texture_ = nullptr;
|
||||
}
|
||||
if (scaled_texture_ != nullptr) {
|
||||
SDL_ReleaseGPUTexture(device_, scaled_texture_);
|
||||
scaled_texture_ = nullptr;
|
||||
@@ -1218,6 +1264,18 @@ namespace Rendering {
|
||||
scaling_mode_ = mode;
|
||||
}
|
||||
|
||||
// setInternalResolution — canvia el multiplicador de resolució interna.
|
||||
// Recrea la textura intermèdia amb les noves dimensions (320·N × 200·N).
|
||||
void SDL3GPUShader::setInternalResolution(int multiplier) {
|
||||
const int NEW = std::max(1, multiplier);
|
||||
if (NEW == internal_res_) return;
|
||||
internal_res_ = NEW;
|
||||
if (is_initialized_ && device_ != nullptr) {
|
||||
SDL_WaitForGPUIdle(device_);
|
||||
recreateInternalTexture();
|
||||
}
|
||||
}
|
||||
|
||||
void SDL3GPUShader::setStretch4_3(bool enabled) {
|
||||
stretch_4_3_ = enabled;
|
||||
if (!is_initialized_ || device_ == nullptr) return;
|
||||
@@ -1263,6 +1321,10 @@ namespace Rendering {
|
||||
SDL_ReleaseGPUTexture(device_, scene_texture_);
|
||||
scene_texture_ = nullptr;
|
||||
}
|
||||
if (internal_texture_ != nullptr) {
|
||||
SDL_ReleaseGPUTexture(device_, internal_texture_);
|
||||
internal_texture_ = nullptr;
|
||||
}
|
||||
// scaled_texture_ se libera aquí; se recreará en el primer render() con el factor correcto
|
||||
if (scaled_texture_ != nullptr) {
|
||||
SDL_ReleaseGPUTexture(device_, scaled_texture_);
|
||||
@@ -1305,10 +1367,15 @@ namespace Rendering {
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_Log("SDL3GPUShader: reinit — scene %dx%d, SS %s (scaled se creará en render)",
|
||||
// Recrea la textura interna si internal_res_ > 1 — manté coherència
|
||||
// en canvis d'SS que passen per reinitTexturesAndBuffer().
|
||||
recreateInternalTexture();
|
||||
|
||||
SDL_Log("SDL3GPUShader: reinit — scene %dx%d, SS %s, internal ×%d (scaled se creará en render)",
|
||||
game_width_,
|
||||
game_height_,
|
||||
oversample_ > 1 ? "on" : "off");
|
||||
oversample_ > 1 ? "on" : "off",
|
||||
internal_res_);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1379,4 +1446,39 @@ namespace Rendering {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// recreateInternalTexture — libera y recrea internal_texture_ para el
|
||||
// multiplicador internal_res_ actual. Si val 1, allibera i queda a nullptr
|
||||
// (el pipeline ometrà la còpia al següent render).
|
||||
// ---------------------------------------------------------------------------
|
||||
auto SDL3GPUShader::recreateInternalTexture() -> bool {
|
||||
if (internal_texture_ != nullptr) {
|
||||
SDL_ReleaseGPUTexture(device_, internal_texture_);
|
||||
internal_texture_ = nullptr;
|
||||
}
|
||||
if (internal_res_ <= 1 || device_ == nullptr) return true;
|
||||
|
||||
const int W = game_width_ * internal_res_;
|
||||
const int H = game_height_ * internal_res_;
|
||||
|
||||
SDL_GPUTextureCreateInfo info = {};
|
||||
info.type = SDL_GPU_TEXTURETYPE_2D;
|
||||
info.format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
|
||||
info.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_COLOR_TARGET;
|
||||
info.width = static_cast<Uint32>(W);
|
||||
info.height = static_cast<Uint32>(H);
|
||||
info.layer_count_or_depth = 1;
|
||||
info.num_levels = 1;
|
||||
|
||||
internal_texture_ = SDL_CreateGPUTexture(device_, &info);
|
||||
if (internal_texture_ == nullptr) {
|
||||
SDL_Log("SDL3GPUShader: failed to create internal texture %dx%d (×%d): %s",
|
||||
W, H, internal_res_, SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_Log("SDL3GPUShader: internal texture %dx%d (×%d)", W, H, internal_res_);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Rendering
|
||||
|
||||
@@ -126,6 +126,9 @@ namespace Rendering {
|
||||
texture_filter_linear_ = (filter == Options::TextureFilter::LINEAR);
|
||||
}
|
||||
|
||||
// Multiplicador de resolució interna (1 = off).
|
||||
void setInternalResolution(int multiplier) override;
|
||||
|
||||
private:
|
||||
static auto createShaderMSL(SDL_GPUDevice* device,
|
||||
const char* msl_source,
|
||||
@@ -146,6 +149,7 @@ namespace Rendering {
|
||||
auto createCrtPiPipeline() -> bool; // Pipeline dedicado para el shader CrtPi
|
||||
auto reinitTexturesAndBuffer() -> bool; // Recrea scene_texture_ y upload_buffer_
|
||||
auto recreateScaledTexture(int factor) -> bool; // Recrea scaled_texture_ para factor dado
|
||||
auto recreateInternalTexture() -> bool; // Recrea internal_texture_ (res interna × N)
|
||||
static auto calcSsFactor(float zoom) -> int; // Primer múltiplo de 3 >= zoom (mín 3)
|
||||
// Devuelve el mejor present mode disponible: IMMEDIATE > MAILBOX > VSYNC
|
||||
[[nodiscard]] auto bestPresentMode(bool vsync) const -> SDL_GPUPresentMode;
|
||||
@@ -158,6 +162,7 @@ namespace Rendering {
|
||||
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 joc (game_width_ × game_height_)
|
||||
SDL_GPUTexture* internal_texture_ = nullptr; // Resolució interna ampliada (game·N × game·N), si N>1
|
||||
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;
|
||||
@@ -173,6 +178,7 @@ namespace Rendering {
|
||||
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)
|
||||
int downscale_algo_ = 1; // 0 = bilinear legacy, 1 = Lanczos2, 2 = Lanczos3
|
||||
int internal_res_ = 1; // Multiplicador de resolució interna (1 = off)
|
||||
std::string driver_name_;
|
||||
std::string preferred_driver_; // Driver preferido; vacío = auto (SDL elige)
|
||||
bool is_initialized_ = false;
|
||||
|
||||
@@ -177,6 +177,13 @@ namespace Rendering {
|
||||
* @brief Filtre de textura global per a l'upscale final (sempre aplicat).
|
||||
*/
|
||||
virtual void setTextureFilter(Options::TextureFilter /*filter*/) {}
|
||||
|
||||
/**
|
||||
* @brief Multiplicador enter de la "resolució interna": fa un NN upscale
|
||||
* de scene (320×200) a 320·N × 200·N i la pipeline downstream
|
||||
* parteix d'aquesta textura. 1 = off (sense còpia addicional).
|
||||
*/
|
||||
virtual void setInternalResolution(int /*multiplier*/) {}
|
||||
};
|
||||
|
||||
} // namespace Rendering
|
||||
|
||||
Reference in New Issue
Block a user