fix(play-setup): persist per-game resolution + add independent Frame Rate row - #261
Open
FabianZettl wants to merge 2 commits into
Open
fix(play-setup): persist per-game resolution + add independent Frame Rate row#261FabianZettl wants to merge 2 commits into
FabianZettl wants to merge 2 commits into
Conversation
added 2 commits
August 29, 2026 13:55
The Where It Runs and Tuning rows in Play Setup already persist their per-game choice (NovaLaunchModeOverrides, AutoQualityProfilePreferences), but Resolution did not - chosenResolution was a plain Activity-scoped Compose state field with no backing store, so it reset to the planner's recommendation every time NovaGameDetailActivity was recreated (e.g. after a session ends and the game detail screen is reopened). Adds NovaResolutionOverrides, mirroring NovaLaunchModeOverrides: only the chosen NovaDisplayResolutionChoice.id is persisted (SharedPreferences, keyed per game), since the choice objects themselves are rebuilt fresh from the planner on every open. chooseResolution() now saves the id, and chosenResolution's initial state resolves the saved id back against that open's planner.visibleChoices. Testing: - ./gradlew :app:compileRootDebugKotlin - builds clean - Built and installed on a real device (AYN Thor, debug build). Set Crisis Core's resolution to Performance (960x540) in Play Setup, force-stopped Nova entirely (not just backgrounded), relaunched, and reopened Play Setup for the same game - resolution was still 960x540 with the "Chosen here - applies at launch" caption. Confirms the fix survives a full process death, not just an Activity recreation.
Resolution and frame rate were coupled: each resolution choice from the planner carries its own paired rate (e.g. "1440x810x60"), and the only way to pin an fps independently of that was the global Tuning = High FPS profile plus the app-wide Settings frame rate - there was no per-game way to say "this resolution, but 90fps". The engine to compose an independent fps into the launch blob already existed (NovaLaunchStreamOverride.compose's fpsOverride parameter, previously fed only by highFpsPin()); this just gives Play Setup a second entry point into it. Adds a Frame Rate row between Resolution and Tuning, offering Auto plus 30/60/90/120 fps. An explicit pick here composes over whichever fps the resolution choice (or the host's plan) would have used and wins over the implicit Tuning = High FPS pin, since a pin made by name is a more specific answer than one inferred from a quality profile. Persists per-game via NovaFrameRateOverrides, mirroring the resolution override fix earlier in this branch - same store, same lifecycle. Testing: - ./gradlew :app:compileRootDebugKotlin - builds clean - Built and installed on a real device (AYN Thor, debug build). For Crisis Core -FINAL FANTASY VII- REUNION: set Frame Rate to 90 FPS independently of Resolution (left at 1920x1080), force-stopped Nova entirely, relaunched, reopened Play Setup - Frame Rate still read 90 FPS with "Chosen here - applies at launch". Set it back to Auto and confirmed the row correctly fell back to deriving the rate from the resolution choice again (60 FPS, matching 1920x1080's own paired rate) rather than getting stuck on the last pin.
Contributor
Author
|
Added a second, related change on top of the resolution-persistence fix: an independent Frame Rate row in Play Setup, between Resolution and Tuning. WhyResolution and frame rate were coupled - each resolution choice from the planner carries its own paired rate (e.g. The engine to compose an independent fps into the launch blob already existed ( What this does
Testing
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
On the Play Setup screen, Where It Runs and Tuning already persist their per-game choice across sessions (
NovaLaunchModeOverrides,AutoQualityProfilePreferences). Resolution did not: picking a resolution worked for the launch it was chosen for, but after a disconnect/reconnect (or any timeNovaGameDetailActivitygets recreated) the row silently reset back to the planner's recommendation, as if the choice had never been made.Root cause
chosenResolution(NovaGameDetailActivity.kt) was a plainmutableStateOf<NovaDisplayResolutionChoice?>(null)Activity-scoped Compose state field with no backing store at all - it only ever got read once bylaunchOptimization()to build the one-shot launch override blob, then died with the Activity onfinish(). There was no persistence path to hook into; the row just looked like a setting without being one.What this does
Adds
NovaResolutionOverrides, mirroring the existingNovaLaunchModeOverridespattern: only the chosenNovaDisplayResolutionChoice.idis persisted (SharedPreferences, keyed per game), since theNovaDisplayResolutionChoiceobjects themselves are rebuilt fresh from the planner on every screen open anyway.chooseResolution()now saves the id alongside updating the in-memory state.chosenResolution's initial value is resolved from the saved id against that open'sresolutionPlanner(game).visibleChoicesvia a newloadResolutionOverride()helper.No changes to the picker UI, the planner, or the launch-time override blob - this only fixes what happens to the choice between launches.
Testing
./gradlew :app:compileRootDebugKotlin- builds clean