Add "Output Resampling" option (#1541)

Adds an Output Resampling option to the Video tab to allow choosing between the old Bilinear sampler and a new Area sampler. Area sampling produces a much cleaner, softer image when downscaling, and a significantly sharper image when upscaling. This can also serve as a halfway decent anti-aliasing substitute until we have a more proper implementation.
This commit is contained in:
Irastris
2026-05-17 22:26:10 -04:00
committed by GitHub
parent 7919c6110d
commit c516b91476
8 changed files with 64 additions and 1 deletions
+1
View File
@@ -176,6 +176,7 @@ namespace dusk::config {
template class ConfigImpl<dusk::GameLanguage>;
template class ConfigImpl<dusk::GyroMode>;
template class ConfigImpl<dusk::FrameInterpMode>;
template class ConfigImpl<dusk::Resampler>;
}
void dusk::config::Register(ConfigVarBase& configVar) {
+2
View File
@@ -63,6 +63,7 @@ UserSettings g_userSettings = {
.enableFrameInterpolation {"game.enableFrameInterpolation", FrameInterpMode::Off},
.internalResolutionScale {"game.internalResolutionScale", 0},
.shadowResolutionMultiplier {"game.shadowResolutionMultiplier", 1},
.resampler {"game.resampler", Resampler::Bilinear},
.enableDepthOfField {"game.enableDepthOfField", true},
.enableMapBackground {"game.enableMapBackground", true},
.disableCutscenePillarboxing {"game.disableCutscenePillarboxing", false},
@@ -223,6 +224,7 @@ void registerSettings() {
Register(g_userSettings.game.disableWaterRefraction);
Register(g_userSettings.game.enableTextureReplacements);
Register(g_userSettings.game.internalResolutionScale);
Register(g_userSettings.game.resampler);
Register(g_userSettings.game.shadowResolutionMultiplier);
Register(g_userSettings.game.enableDepthOfField);
Register(g_userSettings.game.enableMapBackground);
+27
View File
@@ -3,6 +3,7 @@
#include "Z2AudioLib/Z2SeMgr.h"
#include "m_Do/m_Do_audio.h"
#include <aurora/aurora.h>
#include <dolphin/gx/GXAurora.h>
#include <dolphin/vi.h>
#include <fmt/format.h>
@@ -43,6 +44,8 @@ int get_value(GraphicsOption option) {
return getSettings().game.internalResolutionScale.getValue();
case GraphicsOption::ShadowResolution:
return getSettings().game.shadowResolutionMultiplier.getValue();
case GraphicsOption::Resampler:
return static_cast<int>(getSettings().game.resampler.getValue());
case GraphicsOption::BloomMode:
return static_cast<int>(getSettings().game.bloomMode.getValue());
case GraphicsOption::BloomMultiplier:
@@ -62,6 +65,22 @@ void set_value(GraphicsOption option, int value) {
case GraphicsOption::ShadowResolution:
getSettings().game.shadowResolutionMultiplier.setValue(value);
break;
case GraphicsOption::Resampler: {
const auto sampler = static_cast<Resampler>(std::clamp(value,
static_cast<int>(Resampler::Bilinear),
static_cast<int>(Resampler::Area)));
getSettings().game.resampler.setValue(sampler);
switch (sampler) {
case Resampler::Area:
aurora_set_resampler(SAMPLER_AREA);
break;
case Resampler::Bilinear:
default:
aurora_set_resampler(SAMPLER_BILINEAR);
break;
}
break;
}
case GraphicsOption::BloomMode:
getSettings().game.bloomMode.setValue(static_cast<BloomMode>(std::clamp(
value, static_cast<int>(BloomMode::Off), static_cast<int>(BloomMode::Dusk))));
@@ -177,6 +196,14 @@ Rml::String format_graphics_setting_value(GraphicsOption option, int value) {
}
case GraphicsOption::ShadowResolution:
return fmt::format("{}×", value);
case GraphicsOption::Resampler:
switch (static_cast<Resampler>(value)) {
case Resampler::Bilinear:
return "Bilinear";
case Resampler::Area:
return "Area";
}
break;
case GraphicsOption::BloomMode:
switch (static_cast<BloomMode>(value)) {
case BloomMode::Off:
+1
View File
@@ -42,6 +42,7 @@ private:
enum class GraphicsOption {
InternalResolution,
ShadowResolution,
Resampler,
BloomMode,
BloomMultiplier,
};
+11
View File
@@ -357,6 +357,8 @@ const Rml::String kInternalResolutionHelpText =
const Rml::String kShadowResolutionHelpText =
"Configure the shadow-map resolution. Higher values improve shadow quality but increase GPU "
"and memory usage.";
const Rml::String kResamplerHelpText =
"Configure the sampling method used when scaling the internal resolution for final presentation.";
const Rml::String kBloomHelpText =
"Configure the post-processing bloom effect. Classic uses the original bloom pass; Dusklight uses "
"a higher-quality bloom pass.";
@@ -805,6 +807,15 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
.valueMax = 8,
.defaultValue = 1,
}, mPrelaunch);
graphics_tuner_control(*this, leftPane, rightPane, getSettings().game.resampler,
GraphicsTunerProps{
.option = GraphicsOption::Resampler,
.title = "Output Resampling",
.helpText = kResamplerHelpText,
.valueMin = static_cast<int>(Resampler::Bilinear),
.valueMax = static_cast<int>(Resampler::Area),
.defaultValue = static_cast<int>(Resampler::Bilinear),
}, mPrelaunch);
leftPane.add_section("Post-Processing");
graphics_tuner_control(*this, leftPane, rightPane, getSettings().game.bloomMode,
+9
View File
@@ -604,6 +604,15 @@ int game_main(int argc, char* argv[]) {
AuroraSetViewportPolicy(AURORA_VIEWPORT_STRETCH);
}
VISetFrameBufferScale(dusk::getSettings().game.internalResolutionScale.getValue());
switch (dusk::getSettings().game.resampler.getValue()) {
case dusk::Resampler::Area:
aurora_set_resampler(SAMPLER_AREA);
break;
case dusk::Resampler::Bilinear:
default:
aurora_set_resampler(SAMPLER_BILINEAR);
break;
}
dusk::audio::SetMasterVolume(dusk::audio::MasterVolumeToLinear(dusk::getSettings().audio.masterVolume / 100.0f));
dusk::audio::SetEnableReverb(dusk::getSettings().audio.enableReverb);