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
|
"""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
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
ConfigurationData = Dict[str, Any]
class ProfileData(TypedDict):
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 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_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())
if not profiles:
profiles = [("", {})]
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", ""))
config = ConfigurationManager._config_from_profile(profile, global_config)
if config["active_in"]:
profiles[name] = config
return {"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
|