refactor: remove duplicate session frame limiter (#1616)

This commit is contained in:
Kurt Himebauch authored and GitHub committed 2026-10-26 11:25:31 -04:00
1 parent ee1b3b26e1
commit 5d945d7784
47 files changed
+94 -170

No files matched your search

@@ -392,7 +392,6 @@ class FrontEndActivity : ComponentActivity() {
private var theme by mutableStateOf("graphite")
private var appScale by mutableStateOf(com.droiddeck.launcher.core.AppUiPrefs.DEFAULT_SCALE)
private var hdrOn by mutableStateOf(false)
private var fpsLimit by mutableStateOf(0)
private var performanceMode by mutableStateOf(false)
private var upscaler by mutableStateOf(0)
private var upscaleSharpness by mutableStateOf(75)
@@ -1703,7 +1702,7 @@ class FrontEndActivity : ComponentActivity() {
ModeSettings(
mode = mode, resolution = resolution,
panelSize = com.droiddeck.launcher.session.SessionDisplay.panelSize(this),
hdr = hdrOn, hdrReason = hdrReason, fpsLimit = fpsLimit, performanceMode = performanceMode,
hdr = hdrOn, hdrReason = hdrReason, performanceMode = performanceMode,
upscaler = upscaler, upscaleSharpness = upscaleSharpness,
gpuDrivers = drivers.summary(),
touchMode = touchMode,
@@ -1769,7 +1768,6 @@ class FrontEndActivity : ComponentActivity() {
onResolution = { value -> SessionPrefs.setResolutionChoice(this, mode, value); resolution = value },
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 },
onPerformanceMode = { on -> SessionPrefs.setPerformanceMode(this, mode, on); performanceMode = on },
onUpscaler = { m -> SessionPrefs.setUpscaler(this, m); upscaler = m },
onUpscaleSharpness = { pct -> SessionPrefs.setUpscaleSharpness(this, pct); upscaleSharpness = pct },
@@ -2021,7 +2019,6 @@ class FrontEndActivity : ComponentActivity() {
runSteamAtStartup = SessionPrefs.runSteamAtStartup(this)
loadBootMovie()
hdrOn = SessionPrefs.hdr(this, mode)
fpsLimit = SessionPrefs.fpsLimit(this, mode)
performanceMode = SessionPrefs.performanceMode(this, mode)
upscaler = SessionPrefs.upscaler(this)
upscaleSharpness = SessionPrefs.upscaleSharpness(this)
@@ -492,7 +492,6 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
if (!SessionState.running) {
val prefMode = SessionPrefs.prefMode(intent.getStringExtra(SessionService.EXTRA_MODE) ?: SessionService.MODE_STEAM)
SessionState.fpsLimit = SessionPrefs.fpsLimit(this, prefMode)
SessionState.performanceMode = SessionPrefs.performanceMode(this, prefMode)
}
applyOutputPacing()
@@ -1976,7 +1975,7 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
GlassesRendering.mirrorRefreshLimit(glassesSettings, glasses, glassesScreen != null)
private fun outputFpsLimit(): Int = SessionPrefs.compositorFpsLimit(
GlassesRendering.fpsLimit(SessionState.fpsLimit, mirrorRefreshLimit()), SessionState.performanceMode,
mirrorRefreshLimit(), SessionState.performanceMode,
)
private fun applyOutputPacing() {
@@ -32,12 +32,6 @@ internal object GlassesRendering {
return if (hz == 30 && kotlin.math.abs(state.hz - 30f) >= 0.5f) 60 else hz
}
fun fpsLimit(requested: Int, mirrorRefreshLimit: Int): Int = when {
mirrorRefreshLimit <= 0 -> requested
requested <= 0 -> mirrorRefreshLimit
else -> minOf(requested, mirrorRefreshLimit)
}
fun usesGlasses(state: GlassesState, outputActive: Boolean, steamVrRunning: Boolean): Boolean =
outputActive || (steamVrRunning && state.usable)
@@ -903,19 +903,8 @@ object SessionPrefs {
prefs(context).edit().putBoolean("hdr.$mode", on).apply()
}
fun fpsLimit(context: Context, mode: String): Int = prefs(context).getInt("fpsLimit.$mode", 0).coerceAtLeast(0)
fun setFpsLimit(context: Context, mode: String, fps: Int) {
prefs(context).edit().putInt("fpsLimit.$mode", fps.coerceAtLeast(0)).apply()
}
val fpsLimitChoices = listOf(0 to "Off", 30 to "30", 40 to "40", 45 to "45", 60 to "60", 90 to "90", 120 to "120")
fun fpsLimitChoices(context: Context): List<Pair<Int, String>> =
fpsLimitChoices.map { (fps, label) -> fps to if (fps == 0) context.getString(R.string.frame_gen_off) else label }
/**
* Performance mode: without a frame limit, buffers go back to the game as soon as they are replaced
* Performance mode: buffers go back to the game as soon as they are replaced
* instead of once per refresh, and gamescope's WSI layer and winex11 reuse their per-present window checks.
*/
fun performanceMode(context: Context, mode: String): Boolean = prefs(context).getBoolean("performanceMode.$mode", false)
@@ -927,8 +916,9 @@ object SessionPrefs {
/** Compositor frame limit that releases buffers to the game unpaced. */
const val FPS_UNPACED = -1
/** The limit handed to the compositor: Performance mode without a limit is unpaced, a chosen limit still paces. */
fun compositorFpsLimit(limit: Int, performanceMode: Boolean): Int = if (performanceMode && limit <= 0) FPS_UNPACED else limit
/** Mirroring still follows the glasses' refresh rate when Performance mode is on. */
fun compositorFpsLimit(mirrorRefreshLimit: Int, performanceMode: Boolean): Int =
if (performanceMode && mirrorRefreshLimit <= 0) FPS_UNPACED else mirrorRefreshLimit
val upscalerChoices = listOf(
0 to "Linear", 2 to "Nearest", 4 to "AMD FSR 1", 3 to "Snapdragon GSR",
@@ -478,19 +478,16 @@ class SessionService : Service() {
guest.add("DXVK_HDR=1")
Log.i(TAG, "hdr: gamescope --hdr-enabled, DXVK_HDR=1")
}
guest.add("BL_FPS=" + SessionState.fpsLimit)
// gamescope's WSI layer and winex11 reuse their per-present window checks for this long; 0 checks on every present.
val windowCheckMs = if (SessionState.performanceMode) 100 else 0
guest.add("DROIDDECK_WSI_BYPASS_CHECK_MS=$windowCheckMs")
guest.add("WINE_OFFSCREEN_CHECK_MS=$windowCheckMs")
// With a frame limit, gamescope's limiter also holds the games to it instead of only pacing the screen.
guest.add("DROIDDECK_GAME_FRAME_LIMIT=" + (if (SessionState.performanceMode) 1 else 0))
if (SessionPrefs.compositorFpsLimit(SessionState.fpsLimit, SessionState.performanceMode) == SessionPrefs.FPS_UNPACED) {
if (SessionState.performanceMode) {
// gamescope turns buffers around slower than an unpaced game draws; more images keep it drawing.
guest.add("GAMESCOPE_WSI_MIN_IMAGE_COUNT=8")
guest.add("GAMESCOPE_WSI_ENSURE_MIN_IMAGE_COUNT=1")
}
Log.i(TAG, "performance mode " + (if (SessionState.performanceMode) "on" else "off") + ", frame limit " + SessionState.fpsLimit)
Log.i(TAG, "performance mode " + (if (SessionState.performanceMode) "on" else "off"))
guest.add("BL_REFRESH=" + Math.round(SessionState.refreshHz))
guest.add("BL_LOG=" + sessionLog.path)
guest.add("BL_DEBUG_DIR=" + sessionDir.path)
@@ -103,9 +103,6 @@ object SessionState {
@Volatile
var upscaleRatio: Float = 0f
@Volatile
var fpsLimit: Int = 0
@Volatile
var performanceMode = false
@@ -60,7 +60,6 @@ class ModeSettings(
val hdr: Boolean,
val hdrReason: String?,
val gpuDrivers: String? = null,
val fpsLimit: Int = 0,
val performanceMode: Boolean = false,
val upscaler: Int = 0,
val upscaleSharpness: Int = 75,
@@ -117,7 +116,6 @@ class ModeSettingsActions(
val onResolution: (String) -> Unit,
val onHdr: (Boolean) -> Unit,
val onGpuDrivers: () -> Unit = {},
val onFpsLimit: (Int) -> Unit = {},
val onPerformanceMode: (Boolean) -> Unit = {},
val onUpscaler: (Int) -> Unit = {},
val onUpscaleSharpness: (Int) -> Unit = {},
@@ -246,8 +244,6 @@ fun ModeSheetRows(s: ModeSettings, a: ModeSettingsActions, tab: String, extras:
SettingsGroup(stringResource(R.string.display_frame_rate)) {
ToggleRow(host, "performance", stringResource(R.string.mode_performance), stringResource(R.string.mode_performance_hint),
s.performanceMode, onChange = a.onPerformanceMode)
ChoiceRow(host, "fps", stringResource(R.string.mode_fps), stringResource(R.string.common_applies_next_session),
SessionPrefs.fpsLimitChoices(LocalContext.current), s.fpsLimit, note = stringResource(R.string.mode_fps_note), onPick = a.onFpsLimit)
}
SettingsGroup(stringResource(R.string.mode_hdr)) {
ToggleRow(
-2
View File
@@ -247,8 +247,6 @@
<string name="mode_ratio">نسبة أبعاد الشاشة</string>
<string name="mode_ratio_custom">تحددها الدقة المخصّصة.</string>
<string name="mode_ratio_auto">يستخدم الخيار «تلقائي» نسبة 16:9 على الأقل.</string>
<string name="mode_fps">حد الإطارات</string>
<string name="mode_fps_note">يحدّ الجلسة بأكملها. تنتقل الشاشة إلى معدل تحديث يكون من مضاعفات الحد.</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">إخراج HDR10</string>
<string name="mode_hdr_unavailable">غير متاح: %1$s.</string>
@@ -247,8 +247,6 @@
<string name="mode_ratio">Relación de aspecto</string>
<string name="mode_ratio_custom">Definida por la resolución personalizada.</string>
<string name="mode_ratio_auto">Auto usa al menos 16:9.</string>
<string name="mode_fps">Límite de fotogramas</string>
<string name="mode_fps_note">Limita toda la sesión. La pantalla cambia a una tasa de refresco que el límite divide exactamente.</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">Salida HDR10</string>
<string name="mode_hdr_unavailable">No disponible: %1$s.</string>
-2
View File
@@ -247,8 +247,6 @@
<string name="mode_ratio">Пропорции на екрана</string>
<string name="mode_ratio_custom">Определени от персонализираната резолюция.</string>
<string name="mode_ratio_auto">Автоматично използва поне 16:9.</string>
<string name="mode_fps">Ограничение на кадрите</string>
<string name="mode_fps_note">Ограничава цялата сесия. Екранът преминава към честота на опресняване, която се дели точно на ограничението.</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">HDR10 изход</string>
<string name="mode_hdr_unavailable">Не е налично: %1$s.</string>
-2
View File
@@ -247,8 +247,6 @@
<string name="mode_ratio">Poměr stran obrazovky</string>
<string name="mode_ratio_custom">Určen vlastním rozlišením.</string>
<string name="mode_ratio_auto">Automaticky použije alespoň 16:9.</string>
<string name="mode_fps">Omezení snímků</string>
<string name="mode_fps_note">Omezuje celou relaci. Displej přejde na obnovovací frekvenci, kterou lze tímto limitem dělit beze zbytku.</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">Výstup HDR10</string>
<string name="mode_hdr_unavailable">Není k dispozici: %1$s.</string>
-2
View File
@@ -247,8 +247,6 @@
<string name="mode_ratio">Billedformat</string>
<string name="mode_ratio_custom">Angivet af den brugerdefinerede opløsning.</string>
<string name="mode_ratio_auto">Auto bruger mindst 16:9.</string>
<string name="mode_fps">Billedgrænse</string>
<string name="mode_fps_note">Begrænser hele sessionen. Skærmen skifter til en opdateringshastighed, grænsen går lige op med.</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">HDR10-output</string>
<string name="mode_hdr_unavailable">Ikke tilgængelig: %1$s.</string>
-2
View File
@@ -247,8 +247,6 @@
<string name="mode_ratio">Seitenverhältnis</string>
<string name="mode_ratio_custom">Durch die benutzerdefinierte Auflösung festgelegt.</string>
<string name="mode_ratio_auto">Auto verwendet mindestens 16:9.</string>
<string name="mode_fps">Bildratenlimit</string>
<string name="mode_fps_note">Begrenzt die gesamte Sitzung. Das Display wechselt auf eine Bildwiederholrate, die ein ganzzahliges Vielfaches des Limits ist.</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">HDR10-Ausgabe</string>
<string name="mode_hdr_unavailable">Nicht verfügbar: %1$s.</string>
-2
View File
@@ -247,8 +247,6 @@
<string name="mode_ratio">Αναλογία οθόνης</string>
<string name="mode_ratio_custom">Καθορίζεται από την προσαρμοσμένη ανάλυση.</string>
<string name="mode_ratio_auto">Το Αυτόματο χρησιμοποιεί τουλάχιστον 16:9.</string>
<string name="mode_fps">Όριο καρέ</string>
<string name="mode_fps_note">Περιορίζει ολόκληρη τη συνεδρία. Η οθόνη μεταβαίνει σε ρυθμό ανανέωσης που διαιρείται ακριβώς με το όριο.</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">Έξοδος HDR10</string>
<string name="mode_hdr_unavailable">Μη διαθέσιμο: %1$s.</string>
-2
View File
@@ -247,8 +247,6 @@
<string name="mode_ratio">Relación de aspecto</string>
<string name="mode_ratio_custom">Definida por la resolución personalizada.</string>
<string name="mode_ratio_auto">Auto usa al menos 16:9.</string>
<string name="mode_fps">Límite de fotogramas</string>
<string name="mode_fps_note">Limita toda la sesión. La pantalla cambia a una tasa de refresco que el límite divide exactamente.</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">Salida HDR10</string>
<string name="mode_hdr_unavailable">No disponible: %1$s.</string>
-2
View File
@@ -247,8 +247,6 @@
<string name="mode_ratio">Näytön kuvasuhde</string>
<string name="mode_ratio_custom">Määräytyy mukautetun resoluution mukaan.</string>
<string name="mode_ratio_auto">Automaattinen käyttää vähintään 16:9:ää.</string>
<string name="mode_fps">Kuvanopeusraja</string>
<string name="mode_fps_note">Rajoittaa koko istuntoa. Näyttö vaihtaa virkistystaajuuteen, jonka rajoitus jakaa tasan.</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">HDR10-lähtö</string>
<string name="mode_hdr_unavailable">Ei käytettävissä: %1$s.</string>
-2
View File
@@ -247,8 +247,6 @@
<string name="mode_ratio">Format d\'écran</string>
<string name="mode_ratio_custom">Défini par la résolution personnalisée.</string>
<string name="mode_ratio_auto">Le mode automatique utilise au moins 16:9.</string>
<string name="mode_fps">Limite de taux de rafraîchissement</string>
<string name="mode_fps_note">Limite le taux de rafraîchissement de toute la session. Le moniteur basculera vers un taux de rafraîchissement divisible par cette limite.</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">Sortie HDR10</string>
<string name="mode_hdr_unavailable">Non disponible : %1$s.</string>
-2
View File
@@ -247,8 +247,6 @@
<string name="mode_ratio">स्क्रीन रेशियो</string>
<string name="mode_ratio_custom">कस्टम रिज़ॉल्यूशन से तय होता है।</string>
<string name="mode_ratio_auto">ऑटो कम से कम 16:9 इस्तेमाल करता है।</string>
<string name="mode_fps">फ़्रेम सीमा</string>
<string name="mode_fps_note">पूरे सेशन को सीमित करता है। डिस्प्ले उस रिफ्रेश रेट पर चला जाता है जिसे यह सीमा बराबर बांटती है।</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">HDR10 आउटपुट</string>
<string name="mode_hdr_unavailable">उपलब्ध नहीं: %1$s।</string>
-2
View File
@@ -247,8 +247,6 @@
<string name="mode_ratio">Képernyőarány</string>
<string name="mode_ratio_custom">Az egyéni felbontás határozza meg.</string>
<string name="mode_ratio_auto">Az Automatikus legalább 16:9-et használ.</string>
<string name="mode_fps">Képkocka-korlát</string>
<string name="mode_fps_note">Korlátozza a teljes munkamenetet. A kijelző olyan frissítési frekvenciára vált, amelyet a korlát egyenlően oszt.</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">HDR10-kimenet</string>
<string name="mode_hdr_unavailable">Nem elérhető: %1$s.</string>
-2
View File
@@ -247,8 +247,6 @@
<string name="mode_ratio">Rasio layar</string>
<string name="mode_ratio_custom">Ditentukan oleh resolusi khusus.</string>
<string name="mode_ratio_auto">Otomatis menggunakan setidaknya 16:9.</string>
<string name="mode_fps">Batas frame rate</string>
<string name="mode_fps_note">Membatasi seluruh sesi. Tampilan beralih ke refresh rate yang habis dibagi oleh batas ini.</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">Output HDR10</string>
<string name="mode_hdr_unavailable">Tidak tersedia: %1$s.</string>
-2
View File
@@ -247,8 +247,6 @@
<string name="mode_ratio">Proporzioni schermo</string>
<string name="mode_ratio_custom">Definite dalla risoluzione personalizzata.</string>
<string name="mode_ratio_auto">Auto usa almeno 16:9.</string>
<string name="mode_fps">Limite frame</string>
<string name="mode_fps_note">Limita l\'intera sessione. Lo schermo passa a una frequenza di aggiornamento multipla del limite.</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">Uscita HDR10</string>
<string name="mode_hdr_unavailable">Non disponibile: %1$s.</string>
-2
View File
@@ -247,8 +247,6 @@
<string name="mode_ratio">画面比率</string>
<string name="mode_ratio_custom">カスタム解像度で設定されます。</string>
<string name="mode_ratio_auto">自動モードでは少なくとも16:9を使用します。</string>
<string name="mode_fps">フレームレート制限</string>
<string name="mode_fps_note">セッション全体のフレームレートを制限します。モニターはこの上限で割り切れるリフレッシュレートに切り替わります。</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">HDR10出力</string>
<string name="mode_hdr_unavailable">利用不可:%1$s。</string>
-2
View File
@@ -247,8 +247,6 @@
<string name="mode_ratio">화면 비율</string>
<string name="mode_ratio_custom">사용자 정의 해상도로 설정됩니다.</string>
<string name="mode_ratio_auto">자동 모드에서는 최소 16:9를 사용합니다.</string>
<string name="mode_fps">프레임 속도 제한</string>
<string name="mode_fps_note">세션 전체의 프레임 속도를 제한합니다. 모니터는 이 상한으로 나누어떨어지는 주사율로 전환됩니다.</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">HDR10 출력</string>
<string name="mode_hdr_unavailable">사용 불가: %1$s.</string>
-2
View File
@@ -247,8 +247,6 @@
<string name="mode_ratio">Nisbah skrin</string>
<string name="mode_ratio_custom">Ditetapkan oleh resolusi tersuai.</string>
<string name="mode_ratio_auto">Auto menggunakan sekurang-kurangnya 16:9.</string>
<string name="mode_fps">Had bingkai</string>
<string name="mode_fps_note">Mengehadkan keseluruhan sesi. Paparan beralih kepada kadar penyegaran yang boleh dibahagi sama rata dengan had ini.</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">Output HDR10</string>
<string name="mode_hdr_unavailable">Tidak tersedia: %1$s.</string>
-2
View File
@@ -247,8 +247,6 @@
<string name="mode_ratio">Skjermforhold</string>
<string name="mode_ratio_custom">Angis av den egendefinerte oppløsningen.</string>
<string name="mode_ratio_auto">Auto bruker minst 16:9.</string>
<string name="mode_fps">Bildefrekvensgrense</string>
<string name="mode_fps_note">Begrenser hele sesjonen. Skjermen bytter til en oppdateringsfrekvens som grensen går opp i.</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">HDR10-utgang</string>
<string name="mode_hdr_unavailable">Ikke tilgjengelig: %1$s.</string>
-2
View File
@@ -247,8 +247,6 @@
<string name="mode_ratio">Beeldverhouding</string>
<string name="mode_ratio_custom">Ingesteld door de aangepaste resolutie.</string>
<string name="mode_ratio_auto">Automatisch gebruikt minimaal 16:9.</string>
<string name="mode_fps">Framelimiet</string>
<string name="mode_fps_note">Begrenst de hele sessie. Het scherm schakelt naar een verversingssnelheid die een veelvoud van de limiet is.</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">HDR10-uitvoer</string>
<string name="mode_hdr_unavailable">Niet beschikbaar: %1$s.</string>
-2
View File
@@ -247,8 +247,6 @@
<string name="mode_ratio">Proporcje ekranu</string>
<string name="mode_ratio_custom">Ustalane przez własną rozdzielczość.</string>
<string name="mode_ratio_auto">Auto używa co najmniej 16:9.</string>
<string name="mode_fps">Limit klatek</string>
<string name="mode_fps_note">Ogranicza całą sesję. Wyświetlacz przełącza się na częstotliwość odświeżania będącą wielokrotnością limitu.</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">Wyjście HDR10</string>
<string name="mode_hdr_unavailable">Niedostępne: %1$s.</string>
@@ -247,8 +247,6 @@
<string name="mode_ratio">Proporção do ecrã</string>
<string name="mode_ratio_custom">Definida pela resolução personalizada.</string>
<string name="mode_ratio_auto">Automático utiliza pelo menos 16:9.</string>
<string name="mode_fps">Limite de fotogramas</string>
<string name="mode_fps_note">Limita toda a sessão. O ecrã muda para uma taxa de atualização que seja múltipla do limite.</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">Saída HDR10</string>
<string name="mode_hdr_unavailable">Indisponível: %1$s.</string>
-2
View File
@@ -247,8 +247,6 @@
<string name="mode_ratio">Proporção da tela</string>
<string name="mode_ratio_custom">Definida pela resolução personalizada.</string>
<string name="mode_ratio_auto">O Automático usa no mínimo 16:9.</string>
<string name="mode_fps">Limite de quadros</string>
<string name="mode_fps_note">Limita a sessão inteira. A tela muda para uma taxa de atualização que seja múltipla do limite.</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">Saída HDR10</string>
<string name="mode_hdr_unavailable">Indisponível: %1$s.</string>
-2
View File
@@ -247,8 +247,6 @@
<string name="mode_ratio">Raport de ecran</string>
<string name="mode_ratio_custom">Stabilit de rezoluția personalizată.</string>
<string name="mode_ratio_auto">Automat folosește cel puțin 16:9.</string>
<string name="mode_fps">Limită de cadre</string>
<string name="mode_fps_note">Limitează întreaga sesiune. Afișajul trece la o rată de reîmprospătare pe care limita o împarte exact.</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">Ieșire HDR10</string>
<string name="mode_hdr_unavailable">Indisponibil: %1$s.</string>
-2
View File
@@ -247,8 +247,6 @@
<string name="mode_ratio">Соотношение сторон</string>
<string name="mode_ratio_custom">Задается пользовательским разрешением.</string>
<string name="mode_ratio_auto">Автоматический режим использует не менее 16:9.</string>
<string name="mode_fps">Ограничение частоты кадров</string>
<string name="mode_fps_note">Ограничивает частоту кадров всей сессии. Экран переключится на частоту обновления, кратную этому пределу.</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">Вывод HDR10</string>
<string name="mode_hdr_unavailable">Недоступно: %1$s.</string>
-2
View File
@@ -247,8 +247,6 @@
<string name="mode_ratio">Bildförhållande</string>
<string name="mode_ratio_custom">Bestäms av den anpassade upplösningen.</string>
<string name="mode_ratio_auto">Auto använder minst 16:9.</string>
<string name="mode_fps">Bildgräns</string>
<string name="mode_fps_note">Begränsar hela sessionen. Skärmen byter till en uppdateringsfrekvens som är en jämn multipel av gränsen.</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">HDR10-utgång</string>
<string name="mode_hdr_unavailable">Inte tillgängligt: %1$s.</string>
-2
View File
@@ -247,8 +247,6 @@
<string name="mode_ratio">อัตราส่วนหน้าจอ</string>
<string name="mode_ratio_custom">กำหนดโดยความละเอียดกำหนดเอง</string>
<string name="mode_ratio_auto">อัตโนมัติใช้อย่างน้อย 16:9</string>
<string name="mode_fps">ขีดจำกัดเฟรม</string>
<string name="mode_fps_note">จำกัดทั้งเซสชัน หน้าจอจะเปลี่ยนไปใช้อัตรารีเฟรชที่หารขีดจำกัดได้พอดี</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">เอาต์พุต HDR10</string>
<string name="mode_hdr_unavailable">ไม่พร้อมใช้งาน: %1$s</string>
-2
View File
@@ -247,8 +247,6 @@
<string name="mode_ratio">Ekran oranı</string>
<string name="mode_ratio_custom">Özel çözünürlüğe göre ayarlanır.</string>
<string name="mode_ratio_auto">Otomatik en az 16:9 kullanır.</string>
<string name="mode_fps">Kare sınırı</string>
<string name="mode_fps_note">Tüm oturumu sınırlar. Ekran, sınırın tam katı olan bir yenileme hızına geçer.</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">HDR10 çıkışı</string>
<string name="mode_hdr_unavailable">Kullanılamıyor: %1$s.</string>
-2
View File
@@ -247,8 +247,6 @@
<string name="mode_ratio">Співвідношення сторін</string>
<string name="mode_ratio_custom">Визначається власною роздільною здатністю.</string>
<string name="mode_ratio_auto">«Авто» використовує щонайменше 16:9.</string>
<string name="mode_fps">Обмеження кадрів</string>
<string name="mode_fps_note">Обмежує весь сеанс. Екран перемикається на частоту оновлення, кратну цьому обмеженню.</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">Вивід HDR10</string>
<string name="mode_hdr_unavailable">Недоступно: %1$s.</string>
-2
View File
@@ -247,8 +247,6 @@
<string name="mode_ratio">Tỷ lệ màn hình</string>
<string name="mode_ratio_custom">Được đặt theo độ phân giải tùy chỉnh.</string>
<string name="mode_ratio_auto">Tự động dùng tối thiểu 16:9.</string>
<string name="mode_fps">Giới hạn khung hình</string>
<string name="mode_fps_note">Giới hạn toàn bộ phiên. Màn hình chuyển sang tốc độ làm mới mà giới hạn chia hết.</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">Xuất HDR10</string>
<string name="mode_hdr_unavailable">Không khả dụng: %1$s.</string>
@@ -247,8 +247,6 @@
<string name="mode_ratio">屏幕比例</string>
<string name="mode_ratio_custom">由自定义分辨率决定。</string>
<string name="mode_ratio_auto">自动模式至少使用 16:9。</string>
<string name="mode_fps">帧率上限</string>
<string name="mode_fps_note">限制整个会话。显示器会切换到能被该上限整除的刷新率。</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">HDR10 输出</string>
<string name="mode_hdr_unavailable">不可用:%1$s。</string>
@@ -247,8 +247,6 @@
<string name="mode_ratio">螢幕比例</string>
<string name="mode_ratio_custom">由自訂解像度決定。</string>
<string name="mode_ratio_auto">自動模式至少使用 16:9。</string>
<string name="mode_fps">幀率限制</string>
<string name="mode_fps_note">限制整個會話的幀率。螢幕會切換至能被此上限整除的刷新率。</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">HDR10 輸出</string>
<string name="mode_hdr_unavailable">不可用:%1$s。</string>
@@ -247,8 +247,6 @@
<string name="mode_ratio">螢幕比例</string>
<string name="mode_ratio_custom">由自訂解析度設定。</string>
<string name="mode_ratio_auto">自動模式至少使用 16:9。</string>
<string name="mode_fps">影格率限制</string>
<string name="mode_fps_note">限制整個工作階段的影格率。顯示器會切換到能夠被該上限整除的重新整理率。</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">HDR10 輸出</string>
<string name="mode_hdr_unavailable">無法使用:%1$s。</string>
+2 -4
View File
@@ -94,7 +94,7 @@
<string name="game_env_xalia">Set to 0 to disable Proton’s gamepad-navigation helper for this game.</string>
<string name="game_env_anticheat_offline">On by default. Starts an Easy Anti-Cheat game without its anti-cheat when the game supports offline play. Turn off to launch through Easy Anti-Cheat as usual. Online modes that require Easy Anti-Cheat remain unavailable.</string>
<string name="game_env_hud">DXVK overlay for D3D9–11. fps displays frame rate; unset hides it.</string>
<string name="game_env_dxvk_config">DXVK settings separated by semicolons. Recent builds use dxvk.maxFrameRate for frame limiting; prefer the session frame limiter for ordinary play.</string>
<string name="game_env_dxvk_config">DXVK settings separated by semicolons. Recent builds use dxvk.maxFrameRate for frame limiting; prefer Steam\'s Quick Access menu frame limiter for ordinary play.</string>
<string name="game_env_cores">CPU cores for this game, such as 0-4 or 0,2,4, in place of the session\'s game cores. Fewer cores run cooler.</string>
<string name="game_env_vkd3d_limit">D3D12 frame-rate limit. 0 disables the limit.</string>
<string name="game_env_glthread">Mesa OpenGL threading. true can help CPU-limited OpenGL games; test per game.</string>
@@ -256,10 +256,8 @@
<string name="mode_ratio">Screen ratio</string>
<string name="mode_ratio_custom">Set by the custom resolution.</string>
<string name="mode_ratio_auto">Auto uses at least 16:9.</string>
<string name="mode_fps">Frame limit</string>
<string name="mode_performance">Performance mode</string>
<string name="mode_performance_hint">With no frame limit, games run as fast as they can: frames go back to the game without waiting for the screen. With a frame limit, games are held to it instead of drawing frames the screen never shows. Either way, gamescope and Wine skip repeated window checks. Uses more power and runs warmer without a limit. Applies next session.</string>
<string name="mode_fps_note">Caps the whole session. The display switches to a refresh rate the cap divides evenly.</string>
<string name="mode_performance_hint">Lets games run without waiting for the screen and reduces repeated window checks in gamescope and Wine. Can use more power and run warmer. Use Steam\'s Quick Access menu to limit Steam games. Applies next session.</string>
<string name="mode_hdr">HDR</string>
<string name="mode_hdr10">HDR10 output</string>
<string name="mode_hdr_unavailable">Not available: %1$s.</string>
@@ -1,9 +1,11 @@
package com.droiddeck.launcher.glasses
import android.content.Context
import android.hardware.display.DisplayManager
import android.view.Display
import androidx.compose.runtime.MutableState
import com.droiddeck.launcher.SessionActivity
import com.droiddeck.launcher.session.SessionPrefs
import com.droiddeck.launcher.session.SessionState
import org.junit.After
import org.junit.Assert.assertEquals
@@ -36,10 +38,14 @@ class GlassesMirrorPacingTest {
val panel = Shadow.extract<ModeDisplay>(activity.display)
panel.supported = arrayOf(mode(1, 60f), mode(2, 90f), mode(3, 120f))
panel.current = panel.supported.last()
SessionState.fpsLimit = 0
SessionState.performanceMode = false
activity.getSharedPreferences("session", Context.MODE_PRIVATE).edit().clear().commit()
}
@After fun cleanup() { SessionState.fpsLimit = 0 }
@After fun cleanup() {
SessionState.performanceMode = false
activity.getSharedPreferences("session", Context.MODE_PRIVATE).edit().clear().commit()
}
@Test fun connectingChangingRateAndDisconnectingUpdatePhonePacingWithoutSavingPreferences() {
apply(connected, settings)
@@ -48,18 +54,25 @@ class GlassesMirrorPacingTest {
expect(2, 90f, 90)
apply(GlassesState(), settings)
expect(3, 120f, 0)
assertEquals(0, SessionState.fpsLimit)
}
@Test fun lowerSavedLimitSurvivesMirroringAndStalePhoneRefreshReports() {
SessionState.fpsLimit = 30
@Test fun retiredSavedLimitsDoNotCapThePhoneOrMirroredOutput() {
activity.getSharedPreferences("session", Context.MODE_PRIVATE).edit()
.putInt("fpsLimit.steam", 30).putInt("fpsLimit.desktop", 40).commit()
apply(connected, settings)
expect(1, 60f, 30)
expect(1, 60f, 60)
apply(connected.copy(hz = 120f), settings)
expect(1, 60f, 30)
expect(1, 60f, 60)
apply(GlassesState(), settings)
expect(3, 120f, 30)
assertEquals(30, SessionState.fpsLimit)
expect(3, 120f, 0)
}
@Test fun performanceModeResumesUnpacedOutputAfterMirrorDisconnects() {
SessionState.performanceMode = true
apply(connected, settings)
expect(1, 60f, 60)
apply(GlassesState(), settings)
expect(3, 120f, SessionPrefs.FPS_UNPACED)
}
@Test fun pacingFollowsThePanelWhenTheSystemKeepsALowerRateThanRequested() {
@@ -58,13 +58,6 @@ class GlassesRenderingTest {
assertEquals(0, GlassesRendering.mirrorRefreshLimit(settings, ultra, true))
}
@Test fun mirrorCapPreservesLowerLimitsAndRestoresTheRequestedLimitAfterDisconnect() {
for (requested in listOf(0, 30, 60, 90, 120)) {
assertEquals(if (requested == 30) 30 else 60, GlassesRendering.fpsLimit(requested, 60))
assertEquals(requested, GlassesRendering.fpsLimit(requested, 0))
}
}
private fun render(settings: GlassesSettings, state: GlassesState = ultra, steamVr: Boolean = false) =
GlassesRendering.cardboard(base, settings, state, outputActive = true, steamVrRunning = steamVr)
@@ -24,19 +24,13 @@ class PerformanceModeTest {
assertFalse(SessionPrefs.performanceMode(app, SessionService.MODE_DESKTOP))
}
@Test fun offKeepsTodaysPacing() {
@Test fun offKeepsDisplayAndMirrorPacing() {
assertEquals(0, SessionPrefs.compositorFpsLimit(0, false))
assertEquals(60, SessionPrefs.compositorFpsLimit(60, false))
}
@Test fun onWithoutALimitIsUnpacedAndAChosenLimitStillPaces() {
@Test fun onIsUnpacedUnlessMirroring() {
assertEquals(SessionPrefs.FPS_UNPACED, SessionPrefs.compositorFpsLimit(0, true))
assertEquals(60, SessionPrefs.compositorFpsLimit(60, true))
}
@Test fun anOldUncappedFrameLimitReadsAsOff() {
session.edit().putInt("fpsLimit.steam", -1).commit()
assertEquals(0, SessionPrefs.fpsLimit(app, SessionService.MODE_STEAM))
assertFalse(SessionPrefs.fpsLimitChoices.any { it.first < 0 })
}
}
@@ -29,7 +29,7 @@ class SettingsResetTest {
SessionPrefs.setTheme(app, "ocean")
SessionPrefs.setUnfoldedControlsWithController(app, true)
SessionPrefs.setNoXalia(app, false)
SessionPrefs.setFpsLimit(app, SessionService.MODE_STEAM, 30)
session.edit().putInt("fpsLimit.steam", 30).commit()
SessionPrefs.setHdr(app, SessionService.MODE_DESKTOP, true)
SessionPrefs.setResolutionChoice(app, SessionService.MODE_STEAM, "1280x800")
SessionPrefs.setGuestHostname(app, "MyDeck")
@@ -48,7 +48,7 @@ class SettingsResetTest {
assertEquals("graphite", SessionPrefs.theme(app))
assertFalse(SessionPrefs.unfoldedControlsWithController(app))
assertTrue(SessionPrefs.noXalia(app))
assertEquals(0, SessionPrefs.fpsLimit(app, SessionService.MODE_STEAM))
assertFalse(session.contains("fpsLimit.steam"))
assertFalse(SessionPrefs.hdr(app, SessionService.MODE_DESKTOP))
assertFalse(SessionPrefs.resolutionChosen(app, SessionService.MODE_STEAM))
assertEquals(SessionPrefs.DEFAULT_GUEST_HOSTNAME, SessionPrefs.guestHostname(app))
+1 -1
View File
@@ -98,7 +98,7 @@ custom components can support a different set of options. Android Wine wrapper,
ALSA-server, Box64 and patched async-DXVK options are identified in the editor as
requiring a different/custom component; custom entries remain allowed. The old
`DXVK_FRAME_RATE` environment variable was removed from current DXVK, so it is
not offered as a suggestion. Prefer the session frame limiter for ordinary play.
not offered as a suggestion. Prefer Steam's Quick Access menu frame limiter for ordinary play.
## Launch path and references
@@ -16,7 +16,7 @@ if [ -n "${BL_LOG:-}" ] && [ -z "${BL_INSIDE:-}" ]; then
fi
exec > >({ dd of="$BL_LOG" oflag=append conv=notrunc iflag=count_bytes count="${BL_LOG_CAP:-268435456}" bs=65536 status=none; cat >/dev/null; }) 2>&1
echo "== droiddeck-session $(date) mode=$mode $*"
echo "== uid=$(id -u) size=${BL_WIDTH:-?}x${BL_HEIGHT:-?} fps=${BL_FPS:-0} icd=${VK_ICD_FILENAMES:-none}"
echo "== uid=$(id -u) size=${BL_WIDTH:-?}x${BL_HEIGHT:-?} icd=${VK_ICD_FILENAMES:-none}"
echo "== gamescope: $(gamescope --version 2>&1 | head -1) at $(command -v gamescope)"
echo "== rootfs: $(cat /.version 2>/dev/null || echo unversioned)"
fi
@@ -70,31 +70,6 @@ steam_update_stuck() {
[ "$steam_updates" -ge 3 ]
}
# gamescope's --framerate-limit lasts only until its first frame, which recomputes the limit
# from the per-app value set over gamescope_control, and Steam resets that value to 0 while it
# starts. Watch gamescope's limiter state file (first byte 1 = limiter on) and set the value
# again whenever it is off; the limiter then makes the WSI layer hold Vulkan games (DXVK and
# VKD3D included) to it. Shell builtins only, so the once-a-second check starts no process.
hold_games_to_frame_limit() {
local fps=$1 file=$2 parent=$3 state sent=0 nap napfd=
command -v gamescopectl >/dev/null || { echo "== gamescope: no gamescopectl, the frame limit paces the screen only"; return 0; }
nap=$(mktemp -u "${XDG_RUNTIME_DIR:-/tmp}/droiddeck-nap.XXXXXX") && mkfifo "$nap" 2>/dev/null \
&& exec {napfd}<>"$nap"; rm -f "$nap"
while kill -0 "$parent" 2>/dev/null; do
if [ -f "$file" ]; then
state=
IFS= read -r -n1 -d '' state < "$file"
if [ "$state" != $'\x01' ] && gamescopectl debug_set_fps_limit "$fps" >/dev/null 2>&1; then
sent=$((sent + 1))
if [ "$sent" = 1 ]; then echo "== gamescope: games held to $fps fps"
elif [ "$sent" -le 4 ]; then echo "== gamescope: the frame limit was reset, set it again"
fi
fi
fi
if [ -n "$napfd" ]; then read -r -t 1 -u "$napfd" _; else sleep 1; fi
done
}
run_gamescope() (
local status_file child_status compositor_status
status_file=$(mktemp "${XDG_RUNTIME_DIR:-/tmp}/droiddeck-steam-exit.XXXXXX") || exit 73
@@ -261,16 +236,7 @@ if [ -z "${BL_INSIDE:-}" ]; then
args+=(-S stretch)
export BL_STRETCHED=1
fi
if [ "${BL_FPS:-0}" -gt 0 ]; then
args+=(-r "$BL_FPS")
# -r alone only paces what reaches the screen: a game without vsync keeps drawing
# hundreds of frames that are thrown away. Performance mode also holds the games to it.
if [ "${DROIDDECK_GAME_FRAME_LIMIT:-0}" = 1 ]; then
game_frame_limit=$BL_FPS
else
echo "== gamescope: frame limit $BL_FPS paces the screen only"
fi
elif [ "${BL_REFRESH:-0}" -gt 0 ]; then
if [ "${BL_REFRESH:-0}" -gt 0 ]; then
args+=(-r "$BL_REFRESH")
fi
[ "$mode" = steam ] && [ "${BL_STEAM_UI:-}" != desktop ] && args+=(-e)
@@ -294,7 +260,6 @@ if [ -z "${BL_INSIDE:-}" ]; then
rm -f "${XDG_RUNTIME_DIR:-/tmp}"/gamescope-limiter-*
export GAMESCOPE_LIMITER_FILE="${XDG_RUNTIME_DIR:-/tmp}/droiddeck-gamescope-limiter"
rm -f "$GAMESCOPE_LIMITER_FILE"
[ -n "${game_frame_limit:-}" ] && hold_games_to_frame_limit "$game_frame_limit" "$GAMESCOPE_LIMITER_FILE" $$ &
if [ "$mode" = steam ]; then
if /usr/local/bin/droiddeck-pipewire start; then
eval "$(/usr/local/bin/droiddeck-pipewire env)"
+55
View File
@@ -0,0 +1,55 @@
"""Steam owns game caps; the nested compositor follows the output refresh rate."""
import os
from pathlib import Path
import subprocess
import tempfile
import unittest
SESSION = Path(__file__).resolve().parents[1] / 'linuxfs/overlay/usr/local/bin/droiddeck-session'
class SessionPacingTest(unittest.TestCase):
def launch(self, refresh, **overrides):
source = SESSION.read_text()
start = source.index(' width=${BL_WIDTH:-1280}')
end = source.index('\nfi\n\nbl_displays()', start)
block = source[start:end].replace('/usr/local/bin/droiddeck-pipewire', 'pipewire_stub')
with tempfile.TemporaryDirectory() as directory:
env = dict(os.environ, XDG_RUNTIME_DIR=directory, BL_REFRESH=str(refresh),
BL_MANGOAPP='0', BL_FPS='0', DROIDDECK_GAME_FRAME_LIMIT='0')
env.update(overrides)
script = '''
set -u
mode=steam
pipewire_stub() { return 1; }
run_gamescope() { printf 'ARG:%s\\n' "$@"; }
''' + block
result = subprocess.run(['bash', '-c', script], env=env, capture_output=True,
text=True, timeout=5)
self.assertEqual(0, result.returncode, result.stderr)
return [line.removeprefix('ARG:') for line in result.stdout.splitlines()
if line.startswith('ARG:')]
def test_display_refresh_sets_the_nested_compositor_rate(self):
for refresh in (60, 90, 120):
with self.subTest(refresh=refresh):
args = self.launch(refresh)
self.assertEqual(str(refresh), args[args.index('-r') + 1])
self.assertIn('-e', args)
def test_retired_cap_variables_do_not_override_the_display_rate(self):
for performance in ('0', '1'):
with self.subTest(performance=performance):
args = self.launch(120, BL_FPS='30', DROIDDECK_GAME_FRAME_LIMIT=performance)
self.assertEqual('120', args[args.index('-r') + 1])
self.assertNotIn('--framerate-limit', args)
def test_retired_cap_cannot_set_the_rate_when_the_display_rate_is_unknown(self):
args = self.launch(0, BL_FPS='30', DROIDDECK_GAME_FRAME_LIMIT='1')
self.assertNotIn('-r', args)
self.assertNotIn('--framerate-limit', args)
if __name__ == '__main__':
unittest.main()