From 19af3bcdd25517b0764e7f74b5621eeafd2edce8 Mon Sep 17 00:00:00 2001 From: magartus Date: Thu, 1 Oct 2026 18:38:02 +0200 Subject: [PATCH] Expose the compositor's upscaler as a session setting The Wayland compositor already carries the X11 renderer's spatial upscalers (effects_chain.c: FSR 1 EASU+RCAS, Snapdragon GSR and its quality variant, NIS, RCAS sharpen) behind WaylandCompositor.nativeSetUpscaler / nativeSetUpscaleSharpness, but nothing calls them since the Winlator-era drawer was removed, so the scene is always enlarged to the panel with a plain linear blit. Add "Upscaler" and "Upscaler sharpness" to the session page's Display group (off by default, 75%), stored in SessionPrefs and applied next to nativeSetScaleMode when the session's surface is set up. The choice is also listed in device.txt. --- .../com/droiddeck/launcher/MainActivity.kt | 7 +++++++ .../com/droiddeck/launcher/SessionActivity.kt | 2 ++ .../droiddeck/launcher/core/DeviceReport.kt | 2 ++ .../launcher/session/SessionPrefs.kt | 20 +++++++++++++++++++ .../launcher/ui/ModeSettingsDialog.kt | 15 ++++++++++++++ 5 files changed, 46 insertions(+) diff --git a/app/src/main/java/com/droiddeck/launcher/MainActivity.kt b/app/src/main/java/com/droiddeck/launcher/MainActivity.kt index 3628375b5..abd38b87c 100644 --- a/app/src/main/java/com/droiddeck/launcher/MainActivity.kt +++ b/app/src/main/java/com/droiddeck/launcher/MainActivity.kt @@ -265,6 +265,8 @@ class MainActivity : ComponentActivity() { private var shapeMode by mutableStateOf(SessionPrefs.SHAPE_AUTO) private var hdrOn by mutableStateOf(false) private var fpsLimit by mutableStateOf(0) + private var upscaler by mutableStateOf(0) + private var upscaleSharpness by mutableStateOf(75) private var hdrReason by mutableStateOf(null) private var touchMode by mutableStateOf(SessionPrefs.TOUCH_AUTO) private var suspendPolicy by mutableStateOf(SessionPrefs.SUSPEND_MANUAL) @@ -950,6 +952,7 @@ class MainActivity : ComponentActivity() { ModeSettings( mode = mode, resolutionCap = resolutionCap, customResolution = customResolution, shapeMode = shapeMode, hdr = hdrOn, hdrReason = hdrReason, fpsLimit = fpsLimit, + upscaler = upscaler, upscaleSharpness = upscaleSharpness, gpuDrivers = drivers.summary(), touchMode = touchMode, suspendPolicy = suspendPolicy, @@ -983,6 +986,8 @@ class MainActivity : ComponentActivity() { onHdr = { on -> SessionPrefs.setHdr(this, mode, on); hdrOn = on }, onGpuDrivers = { openComponents(focusContent = true, tab = com.droiddeck.launcher.ui.GPU_TAB) }, onFpsLimit = { fps -> SessionPrefs.setFpsLimit(this, mode, fps); fpsLimit = fps }, + onUpscaler = { m -> SessionPrefs.setUpscaler(this, m); upscaler = m }, + onUpscaleSharpness = { pct -> SessionPrefs.setUpscaleSharpness(this, pct); upscaleSharpness = pct }, onTouch = { t -> SessionPrefs.setTouchMode(this, t); touchMode = t }, onSuspendPolicy = { policy -> SessionPrefs.setSuspendPolicy(this, mode, policy); suspendPolicy = policy }, onOsc = { o -> SessionPrefs.setOscMode(this, o); oscMode = o }, @@ -1111,6 +1116,8 @@ class MainActivity : ComponentActivity() { shapeMode = SessionPrefs.shapeMode(this) hdrOn = SessionPrefs.hdr(this, mode) fpsLimit = SessionPrefs.fpsLimit(this, mode) + upscaler = SessionPrefs.upscaler(this) + upscaleSharpness = SessionPrefs.upscaleSharpness(this) hdrReason = com.droiddeck.launcher.wayland.HdrSupport.probe(this).reason touchMode = SessionPrefs.touchMode(this) suspendPolicy = SessionPrefs.suspendPolicy(this, mode) diff --git a/app/src/main/java/com/droiddeck/launcher/SessionActivity.kt b/app/src/main/java/com/droiddeck/launcher/SessionActivity.kt index 0f63ca6e6..004a4f71a 100644 --- a/app/src/main/java/com/droiddeck/launcher/SessionActivity.kt +++ b/app/src/main/java/com/droiddeck/launcher/SessionActivity.kt @@ -798,6 +798,8 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback { // Letterbox, never stretch or crop: the output can be a different shape from the panel, // and a game's picture must keep its proportions with bars, not lose its edges. WaylandCompositor.nativeSetScaleMode(SCALE_FIT, ALIGN_CENTER) + WaylandCompositor.nativeSetUpscaler(SessionPrefs.upscaler(this)) + WaylandCompositor.nativeSetUpscaleSharpness(SessionPrefs.upscaleSharpness(this)) // The session's folder, claimed here because the compositor starts before the service and // opens its log once. The compositor reads the path from its environment; setting it after // it has started changes nothing, which is why the service copies the file in at teardown. diff --git a/app/src/main/java/com/droiddeck/launcher/core/DeviceReport.kt b/app/src/main/java/com/droiddeck/launcher/core/DeviceReport.kt index 16a9598e9..457e49cf1 100644 --- a/app/src/main/java/com/droiddeck/launcher/core/DeviceReport.kt +++ b/app/src/main/java/com/droiddeck/launcher/core/DeviceReport.kt @@ -150,6 +150,8 @@ object DeviceReport { k("No GL error checks", SessionPrefs.noGlError(context)) k("Steam Deck mode", SessionPrefs.steamDeckMode(context)) k("Steam controller", SessionPrefs.steamController(context)) + k("Upscaler", (SessionPrefs.upscalerChoices.firstOrNull { it.first == SessionPrefs.upscaler(context) }?.second ?: "Off") + + " (sharpness ${SessionPrefs.upscaleSharpness(context)}%)") k("FEX preset", SessionPrefs.fexPreset(context).ifEmpty { "FEX defaults" }) k("Skip xalia", SessionPrefs.noXalia(context)) k("gamescope realtime", SessionPrefs.gamescopeRealtime(context)) diff --git a/app/src/main/java/com/droiddeck/launcher/session/SessionPrefs.kt b/app/src/main/java/com/droiddeck/launcher/session/SessionPrefs.kt index 9eb8a1d35..c99943851 100644 --- a/app/src/main/java/com/droiddeck/launcher/session/SessionPrefs.kt +++ b/app/src/main/java/com/droiddeck/launcher/session/SessionPrefs.kt @@ -579,6 +579,26 @@ object SessionPrefs { val fpsLimitChoices = listOf(0 to "Off", 30 to "30", 40 to "40", 45 to "45", 60 to "60", 90 to "90", 120 to "120") + val upscalerChoices = listOf( + 0 to "Off", 4 to "AMD FSR 1", 3 to "Snapdragon GSR", 8 to "Snapdragon GSR (quality)", + 7 to "NVIDIA NIS", 6 to "Sharpen only", + ) + + fun upscaler(context: Context): Int = + prefs(context).getInt("upscaler", 0).takeIf { m -> upscalerChoices.any { it.first == m } } ?: 0 + + fun setUpscaler(context: Context, mode: Int) { + prefs(context).edit().putInt("upscaler", mode).apply() + } + + val upscaleSharpnessChoices = listOf(0 to "0%", 25 to "25%", 50 to "50%", 75 to "75%", 100 to "100%") + + fun upscaleSharpness(context: Context): Int = prefs(context).getInt("upscaleSharpness", 75).coerceIn(0, 100) + + fun setUpscaleSharpness(context: Context, pct: Int) { + prefs(context).edit().putInt("upscaleSharpness", pct.coerceIn(0, 100)).apply() + } + /** * The mode whose per-mode settings apply: a program run under gamescope (MODE_RUN) is a * fullscreen session like Steam's, so it takes Steam's display, driver and HDR choices. diff --git a/app/src/main/java/com/droiddeck/launcher/ui/ModeSettingsDialog.kt b/app/src/main/java/com/droiddeck/launcher/ui/ModeSettingsDialog.kt index d9f26e6b4..43494d9b8 100644 --- a/app/src/main/java/com/droiddeck/launcher/ui/ModeSettingsDialog.kt +++ b/app/src/main/java/com/droiddeck/launcher/ui/ModeSettingsDialog.kt @@ -53,6 +53,8 @@ class ModeSettings( val gpuDrivers: String = "Auto", /** Frames per second the session is capped at; 0 = none. */ val fpsLimit: Int = 0, + val upscaler: Int = 0, + val upscaleSharpness: Int = 75, val touchMode: String, val suspendPolicy: String, /** Steam only. */ @@ -105,6 +107,8 @@ class ModeSettingsActions( /** Opens the GPU drivers on the Components page: they are shared by every session. */ val onGpuDrivers: () -> Unit = {}, val onFpsLimit: (Int) -> Unit = {}, + val onUpscaler: (Int) -> Unit = {}, + val onUpscaleSharpness: (Int) -> Unit = {}, val onTouch: (String) -> Unit, val onSuspendPolicy: (String) -> Unit, val onOsc: (String) -> Unit, @@ -177,6 +181,17 @@ fun ModeSettingsPage(s: ModeSettings, a: ModeSettingsActions) { note = "Caps the whole session. The screen switches to a rate the cap divides evenly.", onPick = a.onFpsLimit, ) + ChoiceRow( + host, "upscaler", "Upscaler", "Sharpens the picture where it is enlarged to the screen.", + com.droiddeck.launcher.session.SessionPrefs.upscalerChoices, s.upscaler, + note = "Works only when the session is smaller than the screen; Sharpen only works at any size. Costs a little GPU time.", + onPick = a.onUpscaler, + ) + ChoiceRow( + host, "upscale-sharpness", "Upscaler sharpness", null, + com.droiddeck.launcher.session.SessionPrefs.upscaleSharpnessChoices, s.upscaleSharpness, + enabled = s.upscaler != 0, onPick = a.onUpscaleSharpness, + ) if (editCustom) CustomResolutionDialog( initial = custom, onSave = { size -> editCustom = false; a.onCustomResolution(size) },