Merge pull request #128 from magarto/upscaler-setting

Expose the compositor's upscaler (FSR 1 / GSR / NIS) as a session setting
This commit is contained in:
Kurt Himebauch authored and GitHub committed 2026-10-02 23:29:01 -04:00
commit abdc46c7a7
6 files changed
+69

No files matched your search

@@ -304,6 +304,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<String?>(null)
private var touchMode by mutableStateOf(SessionPrefs.TOUCH_AUTO)
private var suspendPolicy by mutableStateOf(SessionPrefs.SUSPEND_MANUAL)
@@ -988,6 +990,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,
@@ -1042,6 +1045,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 },
@@ -1180,6 +1185,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)
@@ -200,6 +200,8 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
private var pcKeyboardOpen by mutableStateOf(false)
private var hudOn by mutableStateOf(true)
private var fillScreen by mutableStateOf(true)
private var upscaler by mutableStateOf(0)
private var upscaleSharpness by mutableStateOf(75)
private var lossless by mutableStateOf(Lossless.State.NONE)
private val pickLossless = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { r ->
if (r.resultCode == RESULT_OK) InAppFilePicker.pickedFile(r.data)?.let { importLossless(it) }
@@ -430,6 +432,7 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
androidApps = androidApps,
hudOn = hudOn,
fillScreen = if (SessionState.mode == SessionService.MODE_STEAM) fillScreen else null,
upscaler = upscaler, upscaleSharpness = upscaleSharpness,
frameGen = frameGen,
lossless = lossless,
oscMode = oscMode, suspendPolicy = suspendPolicy, touchMode = touchMode,
@@ -440,6 +443,14 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
selectedSecondScreenDisplay = selectedSecondScreenDisplay,
onHud = { on -> SessionPrefs.setHudEnabled(this@SessionActivity, on); hudOn = on; hud.refresh() },
onFillScreen = { on -> SessionPrefs.setForceFullscreen(this@SessionActivity, on); fillScreen = on },
onUpscaler = { m ->
SessionPrefs.setUpscaler(this@SessionActivity, m); upscaler = m
WaylandCompositor.nativeSetUpscaler(m)
},
onUpscaleSharpness = { pct ->
SessionPrefs.setUpscaleSharpness(this@SessionActivity, pct); upscaleSharpness = pct
WaylandCompositor.nativeSetUpscaleSharpness(pct)
},
onFrameGenPick = { mode ->
FrameGen.set(this@SessionActivity, mode)
readPrefs()
@@ -659,6 +670,8 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
private fun readPrefs() {
hudOn = SessionPrefs.hudEnabled(this)
fillScreen = SessionPrefs.forceFullscreen(this)
upscaler = SessionPrefs.upscaler(this)
upscaleSharpness = SessionPrefs.upscaleSharpness(this)
touchMode = SessionPrefs.touchMode(this)
frameGen = FrameGen.mode(this)
fexPreset = SessionPrefs.fexPreset(this)
@@ -815,6 +828,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.
@@ -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))
@@ -601,6 +601,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.
@@ -54,6 +54,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. */
@@ -113,6 +115,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,
@@ -189,6 +193,17 @@ fun ModeSettingsPage(s: ModeSettings, a: ModeSettingsActions) {
note = stringResource(R.string.mode_fps_note),
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) },
@@ -171,6 +171,8 @@ class DrawerActions(
val fexPreset: String,
/** Steam only: games stretched to the screen's size, changed live (null = not Steam). */
val fillScreen: Boolean? = null,
val upscaler: Int = 0,
val upscaleSharpness: Int = 75,
val secondScreenMode: SecondScreenMode,
val secondScreenDisplays: List<SecondScreenDisplay>,
val selectedSecondScreenDisplay: Int,
@@ -192,6 +194,8 @@ class DrawerActions(
val onShape: (String) -> Unit,
val onFexPreset: (String) -> Unit,
val onFillScreen: (Boolean) -> Unit = {},
val onUpscaler: (Int) -> Unit = {},
val onUpscaleSharpness: (Int) -> Unit = {},
val onSecondScreenMode: (SecondScreenMode) -> Unit,
val onSecondScreenDisplay: (Int) -> Unit,
val onLaunchAndroidApp: (HomeApp.LaunchableApp, Int?) -> Unit,
@@ -355,6 +359,12 @@ fun SessionDrawer(open: Boolean, page: Int, controllerActive: Boolean, onPageCha
host, "fill", stringResource(R.string.drawer_fill), null, a.fillScreen,
chipModifier = focus.track(page, "fill"), onChange = a.onFillScreen,
)
ChoiceRow(host, "upscaler", "Upscaler", null,
SessionPrefs.upscalerChoices, a.upscaler,
chipModifier = focus.track(page, "upscaler"), onPick = a.onUpscaler)
ChoiceRow(host, "upscale-sharpness", "Upscaler sharpness", null,
SessionPrefs.upscaleSharpnessChoices, a.upscaleSharpness, enabled = a.upscaler != 0,
chipModifier = focus.track(page, "upscale-sharpness"), onPick = a.onUpscaleSharpness)
val fgOpen = host.open == "fg"
val fgLabel = FrameGen.label(LocalContext.current, a.frameGen)
SettingsRow(stringResource(R.string.frame_gen_title), null, highlighted = fgOpen) {