1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
|
"""Small adapter for the upstream lsfg-vk v2 configuration format."""
import json
import sys
import tomllib
from pathlib import Path
from typing import Any, Dict, TypedDict, cast
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
DEFAULT_PROFILE_NAME = "default"
ConfigurationData = Dict[str, Any]
class ProfileData(TypedDict):
# Internal compatibility field; the public API no longer exposes a
# currently selected profile.
current_profile: str
profiles: Dict[str, Dict[str, Any]]
global_config: Dict[str, Any]
PROFILE_DEFAULTS: Dict[str, Any] = {
"active_in": [],
"pacing_mode": "vsync",
"multiplier": 2,
"flow_scale": 1.0,
"performance_mode": False,
"override_present_mode": True,
"preserve_swapchain_image_count": False,
}
GLOBAL_DEFAULTS: Dict[str, Any] = {"dll": "", "no_fp16": False}
def _toml_value(value: Any) -> str:
if isinstance(value, bool):
return str(value).lower()
if isinstance(value, str):
return json.dumps(value)
if isinstance(value, list):
return "[ " + ", ".join(_toml_value(item) for item in value) + " ]"
return str(value)
def _normalize_active_in(value: Any) -> list[str]:
if value in (None, ""):
return []
if isinstance(value, str):
return [value]
if not isinstance(value, list):
raise ValueError("active_in must be a string or list of strings")
return [str(item) for item in value if str(item)]
class ConfigurationManager:
@staticmethod
def get_defaults() -> Dict[str, Any]:
return {**GLOBAL_DEFAULTS, **PROFILE_DEFAULTS}
@staticmethod
def get_field_names() -> list[str]:
return list(ConfigurationManager.get_defaults())
@staticmethod
def get_field_types() -> Dict[str, str]:
return {
"dll": "string", "no_fp16": "boolean", "active_in": "array",
"pacing_mode": "string", "multiplier": "integer", "flow_scale": "float",
"performance_mode": "boolean", "override_present_mode": "boolean",
"preserve_swapchain_image_count": "boolean",
}
@staticmethod
def validate_config(config: Dict[str, Any]) -> Dict[str, Any]:
result = {**PROFILE_DEFAULTS, **GLOBAL_DEFAULTS}
result.update({key: value for key, value in config.items() if key in result})
result["active_in"] = _normalize_active_in(result.get("active_in"))
result["pacing_mode"] = str(result.get("pacing_mode", "vsync")).lower()
if result["pacing_mode"] != "vsync":
raise ValueError("pacing_mode must be vsync")
result["multiplier"] = int(result["multiplier"])
if result["multiplier"] < 1:
raise ValueError("multiplier must be 1 or greater")
result["flow_scale"] = float(result["flow_scale"])
if not 0.25 <= result["flow_scale"] <= 1.0:
raise ValueError("flow_scale must be between 0.25 and 1.0")
for name in ("no_fp16", "performance_mode", "override_present_mode", "preserve_swapchain_image_count"):
result[name] = bool(result[name])
result["dll"] = str(result.get("dll") or "")
return result
@staticmethod
def _migrate_dll_path(value: Any) -> str:
path_value = str(value or "")
if not path_value:
return ""
path = Path(path_value)
if path.name.lower() in {"lossless.dll", "losslessscaling.dll"}:
return str(path.with_name("lsfg-vk.dll"))
return path_value
@staticmethod
def _config_from_profile(profile: Dict[str, Any], global_config: Dict[str, Any]) -> Dict[str, Any]:
raw = dict(profile)
if "pacing_mode" not in raw and "pacing" in raw:
raw["pacing_mode"] = raw["pacing"]
if "override_present_mode" not in raw and "experimental_present_mode" in raw:
raw["override_present_mode"] = raw["experimental_present_mode"] == "fifo"
raw["dll"] = global_config.get("dll", "")
raw["no_fp16"] = global_config.get("no_fp16", False)
return ConfigurationManager.validate_config(raw)
@staticmethod
def generate_toml_content(config: Dict[str, Any]) -> str:
data: ProfileData = {
"current_profile": DEFAULT_PROFILE_NAME,
"profiles": {DEFAULT_PROFILE_NAME: dict(config)},
"global_config": {"dll": config.get("dll", ""), "no_fp16": config.get("no_fp16", False)},
}
return ConfigurationManager.generate_toml_content_multi_profile(data)
@staticmethod
def generate_toml_content_multi_profile(profile_data: ProfileData) -> str:
global_config = {**GLOBAL_DEFAULTS, **profile_data.get("global_config", {})}
lines = ["version = 2", "", "[global]"]
dll = ConfigurationManager._migrate_dll_path(global_config.get("dll"))
if dll:
lines.append(f"dll = {_toml_value(dll)}")
lines.append(f"allow_fp16 = {_toml_value(not bool(global_config.get('no_fp16', False)))}")
profiles = sorted(profile_data["profiles"].items(), key=lambda item: (item[0] != DEFAULT_PROFILE_NAME, item[0]))
for name, raw in profiles:
config = ConfigurationManager.validate_config({**raw, **global_config})
lines.extend(["", "[[profile]]", f"name = {_toml_value(name)}"])
if config["active_in"]:
lines.append(f"active_in = {_toml_value(config['active_in'])}")
lines.extend([
f"pacing_mode = {_toml_value(config['pacing_mode'])}",
f"multiplier = {config['multiplier']}",
f"flow_scale = {config['flow_scale']}",
f"performance_mode = {_toml_value(config['performance_mode'])}",
f"override_present_mode = {_toml_value(config['override_present_mode'])}",
f"preserve_swapchain_image_count = {_toml_value(config['preserve_swapchain_image_count'])}",
])
return "\n".join(lines) + "\n"
@staticmethod
def parse_toml_content_multi_profile(content: str) -> ProfileData:
data = tomllib.loads(content)
version = data.get("version")
if version not in (1, 2):
raise ValueError("unsupported lsfg-vk configuration version")
raw_global = dict(data.get("global", {}))
global_config = {
"dll": ConfigurationManager._migrate_dll_path(raw_global.get("dll", "")),
"no_fp16": not bool(raw_global.get("allow_fp16", True)),
}
profiles: Dict[str, Dict[str, Any]] = {}
source_profiles = data.get("game", []) if version == 1 else data.get("profile", [])
for profile in source_profiles:
name = str(profile.get("exe" if version == 1 else "name", DEFAULT_PROFILE_NAME))
profiles[name] = ConfigurationManager._config_from_profile(profile, global_config)
if not profiles:
profiles[DEFAULT_PROFILE_NAME] = ConfigurationManager.validate_config(global_config)
elif DEFAULT_PROFILE_NAME not in profiles:
source = profiles.get("decky-lsfg-vk", next(iter(profiles.values())))
profiles[DEFAULT_PROFILE_NAME] = {**source, "active_in": []}
if profiles.get("decky-lsfg-vk", {}).get("active_in", []) == []:
profiles.pop("decky-lsfg-vk", None)
return {"current_profile": DEFAULT_PROFILE_NAME, "profiles": profiles, "global_config": global_config}
@staticmethod
def is_legacy_v1(content: str) -> bool:
try:
return tomllib.loads(content).get("version") == 1
except tomllib.TOMLDecodeError:
return False
@staticmethod
def parse_toml_content(content: str) -> Dict[str, Any]:
data = ConfigurationManager.parse_toml_content_multi_profile(content)
return cast(Dict[str, Any], data["profiles"][DEFAULT_PROFILE_NAME])
|