From 9f29857fd3c76a7a6f23352fbd7b74b838513727 Mon Sep 17 00:00:00 2001 From: purp1e Date: Thu, 6 Nov 2025 21:52:49 +0800 Subject: [PATCH] [feat] multi-resolution and batch testing --- src/components/cstb/FpsTest.tsx | 524 +++++++++++++++++++++++--------- src/store/fps_test.ts | 82 +++++ 2 files changed, 462 insertions(+), 144 deletions(-) diff --git a/src/components/cstb/FpsTest.tsx b/src/components/cstb/FpsTest.tsx index 8aba923..b3fce56 100644 --- a/src/components/cstb/FpsTest.tsx +++ b/src/components/cstb/FpsTest.tsx @@ -221,15 +221,18 @@ export function FpsTest() { const [showResultsTable, setShowResultsTable] = useState(false) const [hardwareInfo, setHardwareInfo] = useState(null) const [isGameRunning, setIsGameRunning] = useState(false) - 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 [batchTestCount, setBatchTestCount] = useState(1) // 批量测试次数(1表示单次测试) + // 从store读取配置数据 + const testNote = fpsTest.state.config.testNote + const customLaunchOption = fpsTest.state.config.customLaunchOption + const isResolutionEnabled = fpsTest.state.config.isResolutionEnabled + const resolutionWidth = fpsTest.state.config.resolutionWidth + const resolutionHeight = fpsTest.state.config.resolutionHeight + const isFullscreen = fpsTest.state.config.isFullscreen + const batchTestCount = fpsTest.state.config.batchTestCount + const isResolutionGroupEnabled = fpsTest.state.config.isResolutionGroupEnabled + const resolutionGroup = fpsTest.state.config.resolutionGroup const [batchTestProgress, setBatchTestProgress] = useState<{ current: number total: number @@ -253,6 +256,8 @@ export function FpsTest() { const testStartTimeRef = useRef(null) // 记录测试开始时的视频设置 const testStartVideoSettingRef = useRef(null) + // 记录当前测试的分辨率信息(用于备注) + const currentTestResolutionRef = useRef<{ width: string; height: string; label: string } | null>(null) // 检测游戏是否运行 const checkGameRunning = useCallback(async () => { @@ -277,14 +282,20 @@ export function FpsTest() { return () => clearInterval(interval) }, [checkGameRunning]) - // 同步当前分辨率到状态(初始化时) + // 同步当前分辨率到store(初始化时) useEffect(() => { if (tool.state.videoSetting) { - setResolutionWidth(tool.state.videoSetting.defaultres || "") - setResolutionHeight(tool.state.videoSetting.defaultresheight || "") - setIsFullscreen(tool.state.videoSetting.fullscreen === "1") + if (!fpsTest.state.config.resolutionWidth && !fpsTest.state.config.resolutionHeight) { + fpsTest.setResolution( + tool.state.videoSetting.defaultres || "", + tool.state.videoSetting.defaultresheight || "" + ) + } + if (fpsTest.state.config.isFullscreen === true && tool.state.videoSetting.fullscreen !== "1") { + fpsTest.setIsFullscreen(tool.state.videoSetting.fullscreen === "1") + } } - }, [tool.state.videoSetting]) + }, [tool.state.videoSetting, fpsTest]) // 获取硬件信息 useEffect(() => { @@ -322,6 +333,7 @@ export function FpsTest() { testStartTimestampRef.current = null testStartTimeRef.current = null testStartVideoSettingRef.current = null + currentTestResolutionRef.current = null // 如果启用了自动关闭游戏,则关闭游戏 if (tool.state.autoCloseGame) { @@ -388,6 +400,11 @@ export function FpsTest() { timeoutRef.current = null } + // 测试结束后读取视频设置(检测分辨率) + if (steam.state.steamDirValid && steam.currentUser()) { + await tool.getVideoConfig(steam.state.steamDir, steam.currentUser()?.steam_id32 || 0) + } + // 提取 avg 和 p1 值 const { avg, p1 } = extractFpsMetrics(parsed.data) @@ -396,20 +413,27 @@ export function FpsTest() { const testDate = now.toISOString() const mapConfig = BENCHMARK_MAPS[selectedMapIndex] - // 使用测试开始时的视频设置(如果有的话),否则使用当前的 - const currentVideoSetting = - testStartVideoSettingRef.current || tool.store.state.videoSetting + // 使用读取到的视频设置(测试结束后读取的) + const currentVideoSetting = tool.store.state.videoSetting // 如果是批量测试,保存结果到批量结果数组,否则直接保存 const currentBatchProgress = batchTestProgress + const currentResolution = currentTestResolutionRef.current if (currentBatchProgress) { const result = { avg, p1 } setBatchTestResults((prev) => [...prev, result]) batchTestResultsRef.current = [...batchTestResultsRef.current, result] - // 添加带批量标识的备注 - const batchNote = testNote - ? `${testNote} [批量${currentBatchProgress.current}/${currentBatchProgress.total}]` + // 添加带批量标识和分辨率信息的备注 + let batchNote = "" + if (currentResolution) { + batchNote = `[分辨率:${currentResolution.label}]` + } + if (testNote) { + batchNote = batchNote ? `${testNote} ${batchNote}` : testNote + } + batchNote = batchNote + ? `${batchNote} [批量${currentBatchProgress.current}/${currentBatchProgress.total}]` : `[批量${currentBatchProgress.current}/${currentBatchProgress.total}]` fpsTest.addResult({ @@ -445,6 +469,13 @@ export function FpsTest() { // 不在这里处理,由startTest的循环处理 } } else { + // 单次测试,添加分辨率信息到备注 + let singleNote = testNote + if (currentResolution) { + const resolutionNote = `[分辨率:${currentResolution.label}]` + singleNote = singleNote ? `${testNote} ${resolutionNote}` : resolutionNote + } + fpsTest.addResult({ id: `${now.getTime()}-${Math.random().toString(36).slice(2, 11)}`, testTime: parsed.timestamp, @@ -470,12 +501,13 @@ export function FpsTest() { monitor: null, } : null, - note: testNote, // 保存备注 + note: singleNote, // 保存备注(包含分辨率信息) }) } - // 清除保存的启动时视频设置 + // 清除保存的启动时视频设置和分辨率信息 testStartVideoSettingRef.current = null + currentTestResolutionRef.current = null if (!silent) { if (avg !== null || p1 !== null) { @@ -530,6 +562,7 @@ export function FpsTest() { tool.state.autoCloseGame, batchTestProgress, testNote, + batchTestResults, ] ) @@ -557,6 +590,7 @@ export function FpsTest() { testStartTimestampRef.current = null testStartTimeRef.current = null testStartVideoSettingRef.current = null + currentTestResolutionRef.current = null addToast({ title: "测试超时(200秒),测试失败", variant: "flat", @@ -599,7 +633,8 @@ export function FpsTest() { const runSingleTest = async ( testIndex: number, totalTests: number, - isFirstTest: boolean = false + isFirstTest: boolean = false, + resolution?: { width: string; height: string; label: string } ): Promise => { if (!steam.state.steamDir || !steam.state.cs2Dir) { return false @@ -642,11 +677,18 @@ export function FpsTest() { // 构建启动参数:基础参数 + 分辨率和全屏设置 + 自定义启动项(如果有) let baseLaunchOption = `-allow_third_party_software -condebug -conclearlog +map_workshop ${mapConfig.workshopId} ${mapConfig.map}` + // 使用传入的分辨率,如果没有则使用store中的分辨率 + const currentResolution = resolution || { + width: resolutionWidth, + height: resolutionHeight, + label: `${resolutionWidth}x${resolutionHeight}`, + } + // 只有在启用分辨率和全屏设置时才添加相关参数 if (isResolutionEnabled) { // 添加分辨率设置(如果有设置) - if (resolutionWidth && resolutionHeight) { - baseLaunchOption += ` -w ${resolutionWidth} -h ${resolutionHeight}` + if (currentResolution.width && currentResolution.height) { + baseLaunchOption += ` -w ${currentResolution.width} -h ${currentResolution.height}` } // 添加全屏/窗口化设置 @@ -669,29 +711,17 @@ export function FpsTest() { server: "worldwide", }) - // 延迟读取视频设置(修复分辨率读取bug) - // 等待游戏启动并应用分辨率设置(5秒) - // 批量测试的第一次不需要等待,因为游戏还没有启动过 - if (!isFirstTest) { - await new Promise((resolve) => setTimeout(resolve, 5000)) - } - - // 重新读取视频配置 - if (steam.state.steamDirValid && steam.currentUser()) { - await tool.getVideoConfig(steam.state.steamDir, steam.currentUser()?.steam_id32 || 0) - // 再等待一下确保配置已更新 - await new Promise((resolve) => setTimeout(resolve, 1000)) - } - - // 保存启动后的视频设置(延迟读取后的) - testStartVideoSettingRef.current = { ...tool.store.state.videoSetting } + // 视频设置将在测试结束后读取(在readResult中) + // 保存当前测试的分辨率信息 + currentTestResolutionRef.current = currentResolution + const resolutionInfo = currentResolution ? ` (${currentResolution.label})` : "" if (totalTests > 1) { addToast({ - title: `批量测试 ${testIndex}/${totalTests}:已启动 ${mapConfig.label} 测试,正在自动监听结果...`, + title: `批量测试 ${testIndex}/${totalTests}${resolutionInfo}:已启动 ${mapConfig.label} 测试,正在自动监听结果...`, }) } else { - addToast({ title: `已启动 ${mapConfig.label} 测试,正在自动监听结果...` }) + addToast({ title: `已启动 ${mapConfig.label} 测试${resolutionInfo},正在自动监听结果...` }) } // 开始自动监听文件更新 @@ -730,6 +760,7 @@ export function FpsTest() { testStartTimestampRef.current = null testStartTimeRef.current = null testStartVideoSettingRef.current = null + currentTestResolutionRef.current = null return false } } @@ -746,14 +777,140 @@ export function FpsTest() { return } + // 检查是否启用分辨率组 + if (isResolutionGroupEnabled && resolutionGroup.length === 0) { + addToast({ title: "请先添加分辨率到分辨率组", variant: "flat", color: "warning" }) + return + } + const totalTests = batchTestCount batchTestAbortRef.current = false setBatchTestResults([]) batchTestResultsRef.current = [] - if (totalTests > 1) { - // 批量测试 - setIsBatchTesting(true) // 设置批量测试状态 + // 如果启用了分辨率组,对每个分辨率进行批量测试 + if (isResolutionGroupEnabled && resolutionGroup.length > 0) { + setIsBatchTesting(true) + const totalResolutions = resolutionGroup.length + const totalTestCount = totalResolutions * totalTests // 总测试次数 + let globalTestIndex = 0 + + // 对每个分辨率进行批量测试 + for (let resIndex = 0; resIndex < totalResolutions; resIndex++) { + if (batchTestAbortRef.current) { + break + } + + const currentResolution = resolutionGroup[resIndex] + // 为当前分辨率重置结果 + batchTestResultsRef.current = [] + + // 对当前分辨率进行批量测试 + for (let i = 1; i <= totalTests; i++) { + if (batchTestAbortRef.current) { + break + } + + globalTestIndex++ + setBatchTestProgress({ current: globalTestIndex, total: totalTestCount }) + + // 执行单次测试,第一次测试跳过5秒等待 + const isFirstTest = resIndex === 0 && i === 1 + const success = await runSingleTest(i, totalTests, isFirstTest, currentResolution) + + if (!success && !batchTestAbortRef.current) { + addToast({ + title: `分辨率 ${currentResolution.label} 批量测试 ${i}/${totalTests} 失败`, + variant: "flat", + color: "warning", + }) + break + } + + // 如果不是最后一次测试,等待5秒再继续 + if (i < totalTests && !batchTestAbortRef.current) { + if (tool.state.autoCloseGame) { + await new Promise((resolve) => setTimeout(resolve, 3000)) + try { + await invoke("kill_game").catch(() => {}) + } catch (error) { + console.error("关闭游戏失败:", error) + } + } + await new Promise((resolve) => setTimeout(resolve, 5000)) + } + } + + // 当前分辨率批量测试完成,计算平均值 + const finalResults = batchTestResultsRef.current + if (!batchTestAbortRef.current && finalResults.length > 0) { + const validResults = finalResults.filter((r) => r.avg !== null || r.p1 !== null) + if (validResults.length > 0) { + const avgAvg = + validResults.reduce((sum, r) => sum + (r.avg || 0), 0) / validResults.length + const avgP1 = validResults.reduce((sum, r) => sum + (r.p1 || 0), 0) / validResults.length + + const now = new Date() + const testDate = now.toISOString() + const month = String(now.getMonth() + 1).padStart(2, "0") + const day = String(now.getDate()).padStart(2, "0") + const hour = String(now.getHours()).padStart(2, "0") + const minute = String(now.getMinutes()).padStart(2, "0") + const second = String(now.getSeconds()).padStart(2, "0") + const testTime = `${month}/${day} ${hour}:${minute}:${second}` + + let averageNote = `[分辨率:${currentResolution.label}]` + if (testNote) { + averageNote = `${testNote} ${averageNote}` + } + averageNote = `${averageNote} [批量${totalTests}次平均]` + + fpsTest.addResult({ + id: `${now.getTime()}-${Math.random().toString(36).slice(2, 11)}`, + testTime, + testDate, + mapName: mapConfig?.name || "unknown", + mapLabel: mapConfig?.label || "未知地图", + avg: avgAvg, + p1: avgP1, + rawResult: `分辨率${currentResolution.label}批量测试${totalTests}次平均值\n平均帧: ${avgAvg.toFixed( + 1 + )}\nP1低帧: ${avgP1.toFixed(1)}`, + videoSetting: tool.store.state.videoSetting, + hardwareInfo: hardwareInfo + ? { + cpu: hardwareInfo.cpus[0]?.brand || null, + cpuCount: hardwareInfo.cpu_count || null, + os: + hardwareInfo.name && hardwareInfo.os_version + ? `${hardwareInfo.name} ${hardwareInfo.os_version}` + : null, + memory: hardwareInfo.total_memory + ? Math.round(hardwareInfo.total_memory / 1024 / 1024 / 1024) + : null, + gpu: null, + monitor: null, + } + : null, + note: averageNote, + }) + + addToast({ + title: `分辨率 ${currentResolution.label} 批量测试完成!平均值:avg ${avgAvg.toFixed( + 1 + )} FPS, p1 ${avgP1.toFixed(1)} FPS`, + color: "success", + }) + } + } + } + + setBatchTestProgress(null) + setBatchTestResults([]) + setIsBatchTesting(false) + } else if (totalTests > 1) { + // 普通批量测试(不使用分辨率组) + setIsBatchTesting(true) setBatchTestProgress({ current: 0, total: totalTests }) for (let i = 1; i <= totalTests; i++) { @@ -773,9 +930,7 @@ export function FpsTest() { // 如果不是最后一次测试,等待5秒再继续 if (i < totalTests && !batchTestAbortRef.current) { - // 确保游戏已关闭(如果自动关闭已启用,readResult中已经关闭了) if (tool.state.autoCloseGame) { - // 等待游戏关闭完成(readResult中延迟2秒关闭) await new Promise((resolve) => setTimeout(resolve, 3000)) try { await invoke("kill_game").catch(() => {}) @@ -783,13 +938,11 @@ export function FpsTest() { console.error("关闭游戏失败:", error) } } - // 等待5秒 await new Promise((resolve) => setTimeout(resolve, 5000)) } } // 批量测试完成,计算平均值 - // 使用ref中收集的结果(确保获取到所有结果) const finalResults = batchTestResultsRef.current if (!batchTestAbortRef.current && finalResults.length > 0) { const validResults = finalResults.filter((r) => r.avg !== null || r.p1 !== null) @@ -850,7 +1003,7 @@ export function FpsTest() { setBatchTestProgress(null) setBatchTestResults([]) - setIsBatchTesting(false) // 批量测试结束,重置状态 + setIsBatchTesting(false) } else { // 单次测试 await runSingleTest(1, 1, true) @@ -965,20 +1118,9 @@ export function FpsTest() { } } - // 切换全屏/窗口化(仅更新本地状态,不修改视频配置文件) - 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) + fpsTest.setResolution(preset.width, preset.height) } return ( @@ -1198,7 +1340,7 @@ export function FpsTest() { } }} aria-label="测试地图选择" - size="md" + size="sm" radius="lg" > {BENCHMARK_MAPS.map((map, index) => ( @@ -1209,13 +1351,13 @@ export function FpsTest() {
- fpsTest.setResolution(val, resolutionHeight)} + isDisabled={isResolutionGroupEnabled ? isMonitoring : (!isResolutionEnabled || isMonitoring)} + className="w-20" + /> + x + fpsTest.setResolution(resolutionWidth, val)} + isDisabled={isResolutionGroupEnabled ? isMonitoring : (!isResolutionEnabled || isMonitoring)} + className="w-20" + /> +
-
- { - if (resolutionWidth && resolutionHeight) { - void handleSetResolution(resolutionWidth, resolutionHeight) - } - }} - /> - x - { - if (resolutionWidth && resolutionHeight) { - void handleSetResolution(resolutionWidth, resolutionHeight) - } - }} - /> -
- - - {/* 全屏/窗口化切换 */} -
-
{/* 对齐标签高度 */} -
@@ -1404,6 +1615,30 @@ export function FpsTest() { 手动读取结果 + {isResolutionGroupEnabled && ( +
+ {resolutionGroup.map((res, index) => ( + { + if (!isMonitoring) { + fpsTest.removeResolutionFromGroup(index) + } + }} + isCloseable={!isMonitoring} + > + {res.label} + + ))} + {resolutionGroup.length === 0 && ( + 暂无分辨率 + )} +
+ )} + {isMonitoring && ( 正在监听中... @@ -1482,3 +1717,4 @@ export function FpsTest() { ) } + diff --git a/src/store/fps_test.ts b/src/store/fps_test.ts index 25d0440..7de2959 100644 --- a/src/store/fps_test.ts +++ b/src/store/fps_test.ts @@ -25,8 +25,26 @@ export interface FpsTestResult { note?: string // 备注(可选,用于向后兼容) } +export interface ResolutionGroupItem { + width: string + height: string + label: string +} + const defaultValue = { results: [] as FpsTestResult[], + // FpsTest配置数据 + config: { + batchTestCount: 1, // 批量测试次数 + isResolutionGroupEnabled: false, // 是否启用分辨率组 + resolutionGroup: [] as ResolutionGroupItem[], // 分辨率组列表 + testNote: "", // 测试备注 + customLaunchOption: "", // 自定义启动项 + isResolutionEnabled: true, // 是否启用分辨率和全屏设置 + resolutionWidth: "", // 分辨率宽度 + resolutionHeight: "", // 分辨率高度 + isFullscreen: true, // 全屏模式 + }, } export const fpsTestStore = store( @@ -46,6 +64,17 @@ export const useFpsTestStore = () => { removeResult, clearResults, updateNote, + // 配置相关方法 + setBatchTestCount, + setIsResolutionGroupEnabled, + setResolutionGroup, + addResolutionToGroup, + removeResolutionFromGroup, + setTestNote, + setCustomLaunchOption, + setIsResolutionEnabled, + setResolution, + setIsFullscreen, } } @@ -74,3 +103,56 @@ const updateNote = (id: string, note: string) => { } } +// 配置相关方法 +const setBatchTestCount = (count: number) => { + fpsTestStore.state.config.batchTestCount = count +} + +const setIsResolutionGroupEnabled = (enabled: boolean) => { + fpsTestStore.state.config.isResolutionGroupEnabled = enabled +} + +const setResolutionGroup = (group: ResolutionGroupItem[]) => { + fpsTestStore.state.config.resolutionGroup = group +} + +const addResolutionToGroup = (resolution: ResolutionGroupItem) => { + // 检查是否已存在相同分辨率 + const exists = fpsTestStore.state.config.resolutionGroup.some( + (r) => r.width === resolution.width && r.height === resolution.height + ) + if (!exists) { + fpsTestStore.state.config.resolutionGroup = [ + ...fpsTestStore.state.config.resolutionGroup, + resolution, + ] + } +} + +const removeResolutionFromGroup = (index: number) => { + fpsTestStore.state.config.resolutionGroup = fpsTestStore.state.config.resolutionGroup.filter( + (_, i) => i !== index + ) +} + +const setTestNote = (note: string) => { + fpsTestStore.state.config.testNote = note +} + +const setCustomLaunchOption = (option: string) => { + fpsTestStore.state.config.customLaunchOption = option +} + +const setIsResolutionEnabled = (enabled: boolean) => { + fpsTestStore.state.config.isResolutionEnabled = enabled +} + +const setResolution = (width: string, height: string) => { + fpsTestStore.state.config.resolutionWidth = width + fpsTestStore.state.config.resolutionHeight = height +} + +const setIsFullscreen = (isFullscreen: boolean) => { + fpsTestStore.state.config.isFullscreen = isFullscreen +} +