diff options
| author | xXJSONDeruloXx <danielhimebauch@gmail.com> | 2025-07-16 10:42:54 -0400 |
|---|---|---|
| committer | xXJSONDeruloXx <danielhimebauch@gmail.com> | 2025-07-16 10:42:54 -0400 |
| commit | eb1c32ee61d4d4098feb1441ea5bf3b73f520780 (patch) | |
| tree | 4aa33d86c429f10f41051b5db0b7a494ff93c082 | |
| parent | 80247f76332d2704e21361192b774f31b1520e57 (diff) | |
| download | decky-lsfg-vk-eb1c32ee61d4d4098feb1441ea5bf3b73f520780.tar.gz decky-lsfg-vk-eb1c32ee61d4d4098feb1441ea5bf3b73f520780.zip | |
initial add of fps cap ui toggle
| -rw-r--r-- | py_modules/lsfg_vk/configuration.py | 27 | ||||
| -rw-r--r-- | py_modules/lsfg_vk/constants.py | 2 | ||||
| -rw-r--r-- | py_modules/lsfg_vk/plugin.py | 5 | ||||
| -rw-r--r-- | py_modules/lsfg_vk/types.py | 1 | ||||
| -rw-r--r-- | src/api/lsfgApi.ts | 3 | ||||
| -rw-r--r-- | src/components/ConfigurationSection.tsx | 27 | ||||
| -rw-r--r-- | src/components/Content.tsx | 20 | ||||
| -rw-r--r-- | src/components/UsageInstructions.tsx | 5 | ||||
| -rw-r--r-- | src/hooks/useLsfgHooks.ts | 14 |
9 files changed, 83 insertions, 21 deletions
diff --git a/py_modules/lsfg_vk/configuration.py b/py_modules/lsfg_vk/configuration.py index cf3c9a3..f18bc35 100644 --- a/py_modules/lsfg_vk/configuration.py +++ b/py_modules/lsfg_vk/configuration.py @@ -67,7 +67,8 @@ class ConfigurationService(BaseService): "hdr": False, "perf_mode": False, "immediate_mode": False, - "disable_vkbasalt": False + "disable_vkbasalt": False, + "frame_cap": 0 } lines = content.split('\n') @@ -107,11 +108,22 @@ class ConfigurationService(BaseService): # Parse DISABLE_VKBASALT elif match := re.match(r'^(#\s*)?export\s+DISABLE_VKBASALT=(\d+)', line): config["disable_vkbasalt"] = not bool(match.group(1)) and match.group(2) == '1' + + # Parse DXVK_FRAME_RATE + elif match := re.match(r'^(#\s*)?export\s+DXVK_FRAME_RATE=(\d+)', line): + if not bool(match.group(1)): # Not commented out + try: + config["frame_cap"] = int(match.group(2)) + except ValueError: + pass + else: + # If it's commented out, frame cap is disabled (0) + config["frame_cap"] = 0 return config def update_config(self, enable_lsfg: bool, multiplier: int, flow_scale: float, - hdr: bool, perf_mode: bool, immediate_mode: bool, disable_vkbasalt: bool) -> ConfigurationResponse: + hdr: bool, perf_mode: bool, immediate_mode: bool, disable_vkbasalt: bool, frame_cap: int) -> ConfigurationResponse: """Update lsfg script configuration Args: @@ -122,6 +134,7 @@ class ConfigurationService(BaseService): perf_mode: Whether to enable performance mode immediate_mode: Whether to enable immediate present mode (disable vsync) disable_vkbasalt: Whether to disable vkbasalt layer + frame_cap: Frame rate cap value (10-60) Returns: ConfigurationResponse with success status @@ -129,7 +142,7 @@ class ConfigurationService(BaseService): try: # Generate script content using template script_content = self._generate_script_content( - enable_lsfg, multiplier, flow_scale, hdr, perf_mode, immediate_mode, disable_vkbasalt + enable_lsfg, multiplier, flow_scale, hdr, perf_mode, immediate_mode, disable_vkbasalt, frame_cap ) # Write the updated script atomically @@ -138,7 +151,7 @@ class ConfigurationService(BaseService): self.log.info(f"Updated lsfg script configuration: enable={enable_lsfg}, " f"multiplier={multiplier}, flow_scale={flow_scale}, hdr={hdr}, " f"perf_mode={perf_mode}, immediate_mode={immediate_mode}, " - f"disable_vkbasalt={disable_vkbasalt}") + f"disable_vkbasalt={disable_vkbasalt}, frame_cap={frame_cap}") return { "success": True, @@ -158,7 +171,7 @@ class ConfigurationService(BaseService): } def _generate_script_content(self, enable_lsfg: bool, multiplier: int, flow_scale: float, - hdr: bool, perf_mode: bool, immediate_mode: bool, disable_vkbasalt: bool) -> str: + hdr: bool, perf_mode: bool, immediate_mode: bool, disable_vkbasalt: bool, frame_cap: int) -> str: """Generate script content from configuration parameters Args: @@ -169,6 +182,7 @@ class ConfigurationService(BaseService): perf_mode: Whether to enable performance mode immediate_mode: Whether to enable immediate present mode disable_vkbasalt: Whether to disable vkbasalt layer + frame_cap: Frame rate cap value (10-60) Returns: Generated script content @@ -180,5 +194,6 @@ class ConfigurationService(BaseService): hdr="export LSFG_HDR=1" if hdr else "# export LSFG_HDR=1", perf_mode="export LSFG_PERF_MODE=1" if perf_mode else "# export LSFG_PERF_MODE=1", immediate_mode="export MESA_VK_WSI_PRESENT_MODE=immediate # - disable vsync" if immediate_mode else "# export MESA_VK_WSI_PRESENT_MODE=immediate # - disable vsync", - disable_vkbasalt="export DISABLE_VKBASALT=1" if disable_vkbasalt else "# export DISABLE_VKBASALT=1" + disable_vkbasalt="export DISABLE_VKBASALT=1" if disable_vkbasalt else "# export DISABLE_VKBASALT=1", + frame_cap=f"export DXVK_FRAME_RATE={frame_cap}" if frame_cap > 0 else "# export DXVK_FRAME_RATE=60" ) diff --git a/py_modules/lsfg_vk/constants.py b/py_modules/lsfg_vk/constants.py index 2440d9d..878ac2f 100644 --- a/py_modules/lsfg_vk/constants.py +++ b/py_modules/lsfg_vk/constants.py @@ -36,6 +36,7 @@ export LSFG_FLOW_SCALE={flow_scale} {perf_mode} {immediate_mode} {disable_vkbasalt} +{frame_cap} # Execute the passed command with the environment variables set exec "$@" @@ -54,3 +55,4 @@ DEFAULT_HDR = False DEFAULT_PERF_MODE = True DEFAULT_IMMEDIATE_MODE = False DEFAULT_DISABLE_VKBASALT = True +DEFAULT_FRAME_CAP = 0 diff --git a/py_modules/lsfg_vk/plugin.py b/py_modules/lsfg_vk/plugin.py index 5fb6c8f..7cda63a 100644 --- a/py_modules/lsfg_vk/plugin.py +++ b/py_modules/lsfg_vk/plugin.py @@ -73,7 +73,7 @@ class Plugin: return self.configuration_service.get_config() async def update_lsfg_config(self, enable_lsfg: bool, multiplier: int, flow_scale: float, - hdr: bool, perf_mode: bool, immediate_mode: bool, disable_vkbasalt: bool) -> Dict[str, Any]: + hdr: bool, perf_mode: bool, immediate_mode: bool, disable_vkbasalt: bool, frame_cap: int) -> Dict[str, Any]: """Update lsfg script configuration Args: @@ -84,12 +84,13 @@ class Plugin: perf_mode: Whether to enable performance mode immediate_mode: Whether to enable immediate present mode (disable vsync) disable_vkbasalt: Whether to disable vkbasalt layer + frame_cap: Frame rate cap value (10-60) Returns: ConfigurationResponse dict with success status """ return self.configuration_service.update_config( - enable_lsfg, multiplier, flow_scale, hdr, perf_mode, immediate_mode, disable_vkbasalt + enable_lsfg, multiplier, flow_scale, hdr, perf_mode, immediate_mode, disable_vkbasalt, frame_cap ) # Plugin lifecycle methods diff --git a/py_modules/lsfg_vk/types.py b/py_modules/lsfg_vk/types.py index 07dd768..9f44cf1 100644 --- a/py_modules/lsfg_vk/types.py +++ b/py_modules/lsfg_vk/types.py @@ -63,6 +63,7 @@ class ConfigurationData(TypedDict): perf_mode: bool immediate_mode: bool disable_vkbasalt: bool + frame_cap: int class ConfigurationResponse(BaseResponse): diff --git a/src/api/lsfgApi.ts b/src/api/lsfgApi.ts index 0a0b502..31ecbb5 100644 --- a/src/api/lsfgApi.ts +++ b/src/api/lsfgApi.ts @@ -35,6 +35,7 @@ export interface LsfgConfig { perf_mode: boolean; immediate_mode: boolean; disable_vkbasalt: boolean; + frame_cap: number; } export interface ConfigResult { @@ -56,6 +57,6 @@ export const checkLsfgVkInstalled = callable<[], InstallationStatus>("check_lsfg export const checkLosslessScalingDll = callable<[], DllDetectionResult>("check_lossless_scaling_dll"); export const getLsfgConfig = callable<[], ConfigResult>("get_lsfg_config"); export const updateLsfgConfig = callable< - [boolean, number, number, boolean, boolean, boolean, boolean], + [boolean, number, number, boolean, boolean, boolean, boolean, number], ConfigUpdateResult >("update_lsfg_config"); diff --git a/src/components/ConfigurationSection.tsx b/src/components/ConfigurationSection.tsx index 34955f8..6c6f50a 100644 --- a/src/components/ConfigurationSection.tsx +++ b/src/components/ConfigurationSection.tsx @@ -8,6 +8,7 @@ interface LsfgConfig { perfMode: boolean; immediateMode: boolean; disableVkbasalt: boolean; + frameCap: number; } interface ConfigurationSectionProps { @@ -19,6 +20,7 @@ interface ConfigurationSectionProps { onPerfModeChange: (value: boolean) => Promise<void>; onImmediateModeChange: (value: boolean) => Promise<void>; onDisableVkbasaltChange: (value: boolean) => Promise<void>; + onFrameCapChange: (value: number) => Promise<void>; } export function ConfigurationSection({ @@ -29,7 +31,8 @@ export function ConfigurationSection({ onHdrChange, onPerfModeChange, onImmediateModeChange, - onDisableVkbasaltChange + onDisableVkbasaltChange, + onFrameCapChange }: ConfigurationSectionProps) { return ( <> @@ -114,6 +117,28 @@ export function ConfigurationSection({ /> </PanelSectionRow> + <PanelSectionRow> + <SliderField + label={`Game Frame Cap ${config.frameCap === 0 ? "(Disabled)" : `(${config.frameCap} FPS)`}`} + description="Limit frame rate using DXVK_FRAME_RATE (0 = disabled)" + value={config.frameCap} + min={0} + max={60} + step={5} + notchCount={7} + notchLabels={[ + { notchIndex: 0, label: "Off" }, + { notchIndex: 1, label: "10" }, + { notchIndex: 2, label: "20" }, + { notchIndex: 3, label: "30" }, + { notchIndex: 4, label: "40" }, + { notchIndex: 5, label: "50" }, + { notchIndex: 6, label: "60" } + ]} + onChange={onFrameCapChange} + /> + </PanelSectionRow> + {/* <PanelSectionRow> <ToggleField label="Disable vkbasalt" diff --git a/src/components/Content.tsx b/src/components/Content.tsx index 39de01b..895b2fc 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -38,37 +38,42 @@ export function Content() { // Configuration change handlers const handleEnableLsfgChange = async (value: boolean) => { setters.setEnableLsfg(value); - await updateConfig(value, config.multiplier, config.flowScale, config.hdr, config.perfMode, config.immediateMode, config.disableVkbasalt); + await updateConfig(value, config.multiplier, config.flowScale, config.hdr, config.perfMode, config.immediateMode, config.disableVkbasalt, config.frameCap); }; const handleMultiplierChange = async (value: number) => { setters.setMultiplier(value); - await updateConfig(config.enableLsfg, value, config.flowScale, config.hdr, config.perfMode, config.immediateMode, config.disableVkbasalt); + await updateConfig(config.enableLsfg, value, config.flowScale, config.hdr, config.perfMode, config.immediateMode, config.disableVkbasalt, config.frameCap); }; const handleFlowScaleChange = async (value: number) => { setters.setFlowScale(value); - await updateConfig(config.enableLsfg, config.multiplier, value, config.hdr, config.perfMode, config.immediateMode, config.disableVkbasalt); + await updateConfig(config.enableLsfg, config.multiplier, value, config.hdr, config.perfMode, config.immediateMode, config.disableVkbasalt, config.frameCap); }; const handleHdrChange = async (value: boolean) => { setters.setHdr(value); - await updateConfig(config.enableLsfg, config.multiplier, config.flowScale, value, config.perfMode, config.immediateMode, config.disableVkbasalt); + await updateConfig(config.enableLsfg, config.multiplier, config.flowScale, value, config.perfMode, config.immediateMode, config.disableVkbasalt, config.frameCap); }; const handlePerfModeChange = async (value: boolean) => { setters.setPerfMode(value); - await updateConfig(config.enableLsfg, config.multiplier, config.flowScale, config.hdr, value, config.immediateMode, config.disableVkbasalt); + await updateConfig(config.enableLsfg, config.multiplier, config.flowScale, config.hdr, value, config.immediateMode, config.disableVkbasalt, config.frameCap); }; const handleImmediateModeChange = async (value: boolean) => { setters.setImmediateMode(value); - await updateConfig(config.enableLsfg, config.multiplier, config.flowScale, config.hdr, config.perfMode, value, config.disableVkbasalt); + await updateConfig(config.enableLsfg, config.multiplier, config.flowScale, config.hdr, config.perfMode, value, config.disableVkbasalt, config.frameCap); }; const handleDisableVkbasaltChange = async (value: boolean) => { setters.setDisableVkbasalt(value); - await updateConfig(config.enableLsfg, config.multiplier, config.flowScale, config.hdr, config.perfMode, config.immediateMode, value); + await updateConfig(config.enableLsfg, config.multiplier, config.flowScale, config.hdr, config.perfMode, config.immediateMode, value, config.frameCap); + }; + + const handleFrameCapChange = async (value: number) => { + setters.setFrameCap(value); + await updateConfig(config.enableLsfg, config.multiplier, config.flowScale, config.hdr, config.perfMode, config.immediateMode, config.disableVkbasalt, value); }; const onInstall = () => { @@ -107,6 +112,7 @@ export function Content() { onPerfModeChange={handlePerfModeChange} onImmediateModeChange={handleImmediateModeChange} onDisableVkbasaltChange={handleDisableVkbasaltChange} + onFrameCapChange={handleFrameCapChange} /> )} diff --git a/src/components/UsageInstructions.tsx b/src/components/UsageInstructions.tsx index bf80630..3589c04 100644 --- a/src/components/UsageInstructions.tsx +++ b/src/components/UsageInstructions.tsx @@ -8,6 +8,7 @@ interface ConfigType { perfMode: boolean; immediateMode: boolean; disableVkbasalt: boolean; + frameCap: number; } interface UsageInstructionsProps { @@ -45,6 +46,10 @@ export function UsageInstructions({ config }: UsageInstructionsProps) { envVars.push("DISABLE_VKBASALT=1"); } + if (config.frameCap > 0) { + envVars.push(`DXVK_FRAME_RATE=${config.frameCap}`); + } + return envVars.length > 0 ? `${envVars.join(" ")} %command%` : "%command%"; }; diff --git a/src/hooks/useLsfgHooks.ts b/src/hooks/useLsfgHooks.ts index ad04305..f765ce6 100644 --- a/src/hooks/useLsfgHooks.ts +++ b/src/hooks/useLsfgHooks.ts @@ -77,6 +77,7 @@ export function useLsfgConfig() { const [perfMode, setPerfMode] = useState<boolean>(true); const [immediateMode, setImmediateMode] = useState<boolean>(false); const [disableVkbasalt, setDisableVkbasalt] = useState<boolean>(true); + const [frameCap, setFrameCap] = useState<number>(0); const loadLsfgConfig = async () => { try { @@ -89,6 +90,7 @@ export function useLsfgConfig() { setPerfMode(result.config.perf_mode); setImmediateMode(result.config.immediate_mode); setDisableVkbasalt(result.config.disable_vkbasalt); + setFrameCap(result.config.frame_cap); console.log("Loaded lsfg config:", result.config); } else { console.log("lsfg config not available, using defaults:", result.error); @@ -105,7 +107,8 @@ export function useLsfgConfig() { newHdr: boolean, newPerfMode: boolean, newImmediateMode: boolean, - newDisableVkbasalt: boolean + newDisableVkbasalt: boolean, + newFrameCap: number ): Promise<ConfigUpdateResult> => { try { const result = await updateLsfgConfig( @@ -115,7 +118,8 @@ export function useLsfgConfig() { newHdr, newPerfMode, newImmediateMode, - newDisableVkbasalt + newDisableVkbasalt, + newFrameCap ); if (!result.success) { toaster.toast({ @@ -145,7 +149,8 @@ export function useLsfgConfig() { hdr, perfMode, immediateMode, - disableVkbasalt + disableVkbasalt, + frameCap }, setters: { setEnableLsfg, @@ -154,7 +159,8 @@ export function useLsfgConfig() { setHdr, setPerfMode, setImmediateMode, - setDisableVkbasalt + setDisableVkbasalt, + setFrameCap }, loadLsfgConfig, updateConfig |
