From e774b31396241f1ab7792377927d0cb7a6ea6199 Mon Sep 17 00:00:00 2001 From: purp1e Date: Thu, 6 Nov 2025 14:26:17 +0800 Subject: [PATCH] [feat] more fps test settings and csv export button --- src/app/prepare/page.tsx | 54 +++-- src/app/test/page.tsx | 10 +- src/components/cstb/FpsTest.tsx | 382 +++++++++++++++++++++++++++----- 3 files changed, 364 insertions(+), 82 deletions(-) diff --git a/src/app/prepare/page.tsx b/src/app/prepare/page.tsx index b586066..f9ab1c0 100644 --- a/src/app/prepare/page.tsx +++ b/src/app/prepare/page.tsx @@ -17,29 +17,39 @@ export default function Page() { className="flex flex-col items-center justify-center w-full h-screen gap-6" data-tauri-drag-region > -

CS工具箱

-

准备环节

+

CS 工具箱

+

配置页面

-
-

Steam所在文件夹

- { - setSteamDir(e.target.value) - steam.setDir(e.target.value) - }} - /> -

CS2所在文件夹

- { - setCs2Dir(e.target.value) - steam.setCsDir(e.target.value) - }} - /> -

当前用户64位SteamID:{steam.currentUser()?.steam_id64}

+
+
+

Steam 安装目录

+ { + setSteamDir(e.target.value) + steam.setDir(e.target.value) + }} + /> +
+
+

CS2 安装目录

+ { + setCs2Dir(e.target.value) + steam.setCsDir(e.target.value) + }} + /> +
+ {steam.currentUser()?.steam_id64 && ( +

+ 当前用户 64 位 Steam ID:{steam.currentUser()?.steam_id64} +

+ )}
) diff --git a/src/app/test/page.tsx b/src/app/test/page.tsx index f2b8534..4be69d8 100644 --- a/src/app/test/page.tsx +++ b/src/app/test/page.tsx @@ -6,7 +6,7 @@ import { useCallback, useState } from "react" export default function Page() { const [buttonDesc, setButtonDesc] = useState( - "Waiting to be clicked. This calls 'on_button_clicked' from Rust.", + "等待点击。这将调用 Rust 中的 'on_button_clicked' 命令。", ) const onButtonClick = () => { invoke("on_button_clicked") @@ -14,7 +14,7 @@ export default function Page() { setButtonDesc(value) }) .catch(() => { - setButtonDesc("Failed to invoke Rust command 'on_button_clicked'") + setButtonDesc("调用 Rust 命令 'on_button_clicked' 失败") }) } @@ -27,7 +27,7 @@ export default function Page() {

- Welcome to{" "} + 欢迎使用{" "}

- Get started by editing{" "} + 开始编辑{" "} src/pages/index.tsx @@ -48,7 +48,7 @@ export default function Page() {

diff --git a/src/components/cstb/FpsTest.tsx b/src/components/cstb/FpsTest.tsx index 9793d4a..4df7487 100644 --- a/src/components/cstb/FpsTest.tsx +++ b/src/components/cstb/FpsTest.tsx @@ -26,11 +26,17 @@ import { ModalFooter, Textarea, useDisclosure, + Dropdown, + DropdownTrigger, + DropdownMenu, + DropdownItem, } from "@heroui/react" import { useState, useEffect, useRef, useCallback } from "react" -import { TestTube, Power, List, Delete, Play, Edit, Check, Close, Square } from "@icon-park/react" +import { TestTube, Power, List, Delete, Play, Edit, Check, Close, Square, DownloadOne } from "@icon-park/react" import { allSysInfo, type AllSystemInfo } from "tauri-plugin-system-info-api" import { ToolButton } from "../window/ToolButton" +import { save } from "@tauri-apps/plugin-dialog" +import { writeTextFile } from "@tauri-apps/plugin-fs" const BENCHMARK_MAPS = [ { @@ -49,6 +55,19 @@ const BENCHMARK_MAPS = [ const TEST_TIMEOUT = 200000 // 200秒超时时间(毫秒) +// 预设分辨率列表 +const PRESET_RESOLUTIONS = [ + { width: "800", height: "600", label: "800x600" }, + { width: "1024", height: "768", label: "1024x768" }, + { width: "1280", height: "960", label: "1280x960" }, + { width: "1440", height: "1080", label: "1440x1080" }, + { width: "1920", height: "1080", label: "1920x1080" }, + { width: "1920", height: "1440", label: "1920x1440" }, + { width: "2560", height: "1440", label: "2560x1440" }, + { width: "2880", height: "2160", label: "2880x2160" }, + { width: "3840", height: "2160", label: "3840x2160" }, +] as const + // 解析性能报告,提取时间戳和性能数据 function parseVProfReport(rawReport: string): { timestamp: string; data: string } | null { if (!rawReport) return null @@ -191,6 +210,11 @@ export function FpsTest() { const [testNote, setTestNote] = useState("") // 测试备注 const [editingNoteId, setEditingNoteId] = useState(null) // 正在编辑的备注ID const [editingNoteValue, setEditingNoteValue] = useState("") // 正在编辑的备注内容 + const [customLaunchOption, setCustomLaunchOption] = useState("") // 自定义启动项 + const [isResolutionEnabled, setIsResolutionEnabled] = useState(true) // 是否启用分辨率和全屏设置 + const [resolutionWidth, setResolutionWidth] = useState("") // 分辨率宽度 + const [resolutionHeight, setResolutionHeight] = useState("") // 分辨率高度 + const [isFullscreen, setIsFullscreen] = useState(true) // 全屏模式(默认全屏) const { isOpen: isNoteModalOpen, onOpen: onNoteModalOpen, onClose: onNoteModalClose } = useDisclosure() const monitoringIntervalRef = useRef(null) const timeoutRef = useRef(null) @@ -223,6 +247,15 @@ export function FpsTest() { return () => clearInterval(interval) }, [checkGameRunning]) + // 同步当前分辨率到状态(初始化时) + useEffect(() => { + if (tool.state.videoSetting) { + setResolutionWidth(tool.state.videoSetting.defaultres || "") + setResolutionHeight(tool.state.videoSetting.defaultresheight || "") + setIsFullscreen(tool.state.videoSetting.fullscreen === "1") + } + }, [tool.state.videoSetting]) + // 获取硬件信息 useEffect(() => { const fetchHardwareInfo = async () => { @@ -515,9 +548,30 @@ export function FpsTest() { testStartVideoSettingRef.current = { ...tool.store.state.videoSetting } try { - const launchOption = `-allow_third_party_software -condebug -conclearlog +map_workshop ${mapConfig.workshopId} ${mapConfig.map}` + // 构建启动参数:基础参数 + 分辨率和全屏设置 + 自定义启动项(如果有) + let baseLaunchOption = `-allow_third_party_software -condebug -conclearlog +map_workshop ${mapConfig.workshopId} ${mapConfig.map}` + + // 只有在启用分辨率和全屏设置时才添加相关参数 + if (isResolutionEnabled) { + // 添加分辨率设置(如果有设置) + if (resolutionWidth && resolutionHeight) { + baseLaunchOption += ` -w ${resolutionWidth} -h ${resolutionHeight}` + } + + // 添加全屏/窗口化设置 + if (isFullscreen) { + baseLaunchOption += ` -fullscreen` + } else { + baseLaunchOption += ` -sw` + } + } + + // 添加自定义启动项(如果有,开头加空格避免粘连) + const launchOption = customLaunchOption.trim() + ? `${baseLaunchOption} ${customLaunchOption.trim()}` + : baseLaunchOption - // 启动游戏 + // 启动游戏(强制使用worldwide国际服) await invoke("launch_game", { steamPath: `${steam.state.steamDir}\\steam.exe`, launchOption: launchOption, @@ -574,11 +628,104 @@ export function FpsTest() { } } + // 导出CSV + const handleExportCSV = async () => { + if (fpsTest.state.results.length === 0) { + addToast({ title: "没有测试数据可导出", color: "warning" }) + return + } + + try { + // 构建CSV内容 + const headers = [ + "测试时间", + "测试地图", + "AVG平均帧", + "P1低帧", + "CPU", + "系统版本", + "GPU", + "内存(GB)", + "分辨率", + "视频设置", + "备注", + ] + + const csvRows = [headers.join(",")] + + for (const result of fpsTest.state.results) { + const row = [ + `"${result.testTime}"`, + `"${result.mapLabel}"`, + result.avg !== null ? result.avg.toFixed(1) : "N/A", + result.p1 !== null ? result.p1.toFixed(1) : "N/A", + `"${result.hardwareInfo?.cpu || "N/A"}"`, + `"${result.hardwareInfo?.os || "N/A"}"`, + `"${result.hardwareInfo?.gpu || "N/A"}"`, + result.hardwareInfo?.memory ? result.hardwareInfo.memory.toString() : "N/A", + result.videoSetting + ? `${result.videoSetting.defaultres}x${result.videoSetting.defaultresheight}` + : "N/A", + `"${formatVideoSettingSummary(result.videoSetting)}"`, + `"${result.note || ""}"`, + ] + csvRows.push(row.join(",")) + } + + const csvContent = csvRows.join("\n") + + // 使用文件保存对话框 + const filePath = await save({ + filters: [ + { + name: "CSV", + extensions: ["csv"], + }, + ], + defaultPath: `fps_test_results_${new Date().toISOString().split("T")[0]}.csv`, + }) + + if (filePath) { + await writeTextFile(filePath, csvContent) + addToast({ title: "导出成功", color: "success" }) + } + } catch (error) { + console.error("导出CSV失败:", error) + addToast({ + title: `导出失败: ${error instanceof Error ? error.message : String(error)}`, + color: "danger", + }) + } + } + + // 切换全屏/窗口化(仅更新本地状态,不修改视频配置文件) + const handleToggleFullscreen = () => { + setIsFullscreen(!isFullscreen) + } + + // 设置分辨率(仅更新本地状态,不修改视频配置文件) + const handleSetResolution = (width: string, height: string) => { + setResolutionWidth(width) + setResolutionHeight(height) + } + + // 应用预设分辨率 + const handlePresetResolution = (preset: { width: string; height: string; label: string }) => { + void handleSetResolution(preset.width, preset.height) + } + return ( - 帧数测试 + +
+ 帧数测试 +
+
{isGameRunning && !tool.state.autoCloseGame && ( 游戏运行中 关闭游戏后从这里启动 @@ -587,6 +734,12 @@ export function FpsTest() {
+ {showResultsTable && ( + + )}

) : ( -
- {/* 表单区域:地图和备注同一行 */} -
- {/* 测试地图 */} +
+ {/* 备注单独一行 - 放在最上面 */} +
- { - if (!isMonitoring) { - setSelectedMapIndex(Number(key)) - } - }} - aria-label="测试地图选择" - size="md" - radius="lg" - > - {BENCHMARK_MAPS.map((map, index) => ( - - ))} - +
+ { + if (!isMonitoring) { + setSelectedMapIndex(Number(key)) + } + }} + aria-label="测试地图选择" + size="md" + radius="lg" + > + {BENCHMARK_MAPS.map((map, index) => ( + + ))} + +
+
+
+ +
+ +
+
+
+ + {/* 启动项占满一行,右侧放置分辨率和全屏切换 */} +
+ {/* 自定义启动项 */} +
+ +
+ +
- {/* 备注 */} -
- - + {/* 分辨率和全屏/窗口化设置 */} +
+ {/* 分辨率设置 */} +
+
+ + + + + + + {PRESET_RESOLUTIONS.map( + (preset: { width: string; height: string; label: string }) => ( + handlePresetResolution(preset)} + > + {preset.label} + + ) + )} + + + +
+
+ { + if (resolutionWidth && resolutionHeight) { + void handleSetResolution(resolutionWidth, resolutionHeight) + } + }} + /> + x + { + if (resolutionWidth && resolutionHeight) { + void handleSetResolution(resolutionWidth, resolutionHeight) + } + }} + /> +
+
+ + {/* 全屏/窗口化切换 */} +
+
{/* 对齐标签高度 */} + +
@@ -819,30 +1086,35 @@ export function FpsTest() { 手动读取结果 - {/* 测试结果显示:测试时间、平均帧、P1低帧 */} - {testResult && testTimestamp && (() => { - const { avg, p1 } = extractFpsMetrics(testResult) - return ( - <> -
-
测试时间
-
{testTimestamp}
-
-
-
-
-
平均帧
-
{avg !== null ? `${avg.toFixed(1)}` : "N/A"}
-
-
-
P1低帧
-
{p1 !== null ? `${p1.toFixed(1)}` : "N/A"}
+ {testResult && + testTimestamp && + (() => { + const { avg, p1 } = extractFpsMetrics(testResult) + return ( + <> +
+
测试时间
+
{testTimestamp}
+
+
+
+
+
平均帧
+
+ {avg !== null ? `${avg.toFixed(1)}` : "N/A"} +
+
+
+
P1低帧
+
+ {p1 !== null ? `${p1.toFixed(1)}` : "N/A"} +
+
-
- - ) - })()} + + ) + })()}
{testResult && (