From e2e37cf9b212bc30277b00503c7c01c149428500 Mon Sep 17 00:00:00 2001 From: osy <50960678+osy@users.noreply.github.com> Date: Tue, 1 Sep 2026 13:20:55 -0700 Subject: [PATCH] config: use linear upscaling except on classic guests The nearest-neighbour default (ba5f7f7f, #3371) was chosen for low-resolution guests, where linear washes out already-chunky text. It is the wrong choice everywhere else: a modern guest rarely upscales by an integer ratio, so nearest duplicates source pixels unevenly and looks blocky rather than sharp -- the complaint behind #6209 and #6572. Default to linear and let the wizard select nearest for the guests the original change was about: Classic Mac OS, Windows 7 and below, and any OS configured for legacy hardware. Legacy hardware is checked on its own rather than folded into the Windows case so that choosing a legacy machine opts in under any OS, and because the summary page can enable it after the Windows page has already latched isWindows10OrHigher. Existing VMs are unaffected: upscalingFilter is a required coding key, so a saved configuration always carries its own value. The UTM 2.x migration path is likewise unchanged, since an absent displayUpscaler already defaulted to linear. Assisted-by: Claude:claude-opus-5 --- Configuration/UTMQemuConfigurationDisplay.swift | 7 ++++++- Platform/Shared/VMWizardState.swift | 11 +++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Configuration/UTMQemuConfigurationDisplay.swift b/Configuration/UTMQemuConfigurationDisplay.swift index 822c354d6b..894e97ae0c 100644 --- a/Configuration/UTMQemuConfigurationDisplay.swift +++ b/Configuration/UTMQemuConfigurationDisplay.swift @@ -28,7 +28,12 @@ struct UTMQemuConfigurationDisplay: Codable, Identifiable { var isDynamicResolution: Bool = true /// Filter to use when upscaling. - var upscalingFilter: QEMUScaler = .nearest + /// + /// Nearest neighbour is only a good fit for low-resolution guests, where it keeps the image + /// sharp instead of washing it out (#3371); the wizard selects it for those. For a modern + /// guest the upscale is rarely an integer ratio, so nearest duplicates source pixels + /// unevenly and looks blocky rather than sharp. + var upscalingFilter: QEMUScaler = .linear /// Filter to use when downscaling. var downscalingFilter: QEMUScaler = .linear diff --git a/Platform/Shared/VMWizardState.swift b/Platform/Shared/VMWizardState.swift index 53f96f2ec4..f4547a9ace 100644 --- a/Platform/Shared/VMWizardState.swift +++ b/Platform/Shared/VMWizardState.swift @@ -475,6 +475,17 @@ struct AlertMessage: Identifiable { config.displays[0].hardware = AnyQEMUConstant(rawValue: newCard)! } } + // Low-resolution guests upscale better with nearest neighbour: linear washes out + // their already-chunky text (#3371). Legacy hardware is checked on its own rather + // than folded into the Windows case so that picking a legacy machine opts in under + // any OS, and because the summary page can enable it after the Windows page has + // latched isWindows10OrHigher. Modern guests keep the linear default, where the + // upscale is a non-integer ratio and nearest looks blocky instead of sharp. + if operatingSystem == .ClassicMacOS || legacyHardware || (operatingSystem == .Windows && !isWindows10OrHigher) { + for i in config.displays.indices { + config.displays[i].upscalingFilter = .nearest + } + } if operatingSystem == .Linux && !isDisplayEnabled { config.displays = [] let newSerial = UTMQemuConfigurationSerial(forArchitecture: systemArchitecture, target: systemTarget)!