[feat] multi-resolution and batch testing

This commit is contained in:
2025-11-06 21:52:49 +08:00
parent dcac1295c6
commit 9f29857fd3
2 changed files with 462 additions and 144 deletions

View File

@@ -221,15 +221,18 @@ export function FpsTest() {
const [showResultsTable, setShowResultsTable] = useState(false) const [showResultsTable, setShowResultsTable] = useState(false)
const [hardwareInfo, setHardwareInfo] = useState<AllSystemInfo | null>(null) const [hardwareInfo, setHardwareInfo] = useState<AllSystemInfo | null>(null)
const [isGameRunning, setIsGameRunning] = useState(false) const [isGameRunning, setIsGameRunning] = useState(false)
const [testNote, setTestNote] = useState<string>("") // 测试备注
const [editingNoteId, setEditingNoteId] = useState<string | null>(null) // 正在编辑的备注ID const [editingNoteId, setEditingNoteId] = useState<string | null>(null) // 正在编辑的备注ID
const [editingNoteValue, setEditingNoteValue] = useState<string>("") // 正在编辑的备注内容 const [editingNoteValue, setEditingNoteValue] = useState<string>("") // 正在编辑的备注内容
const [customLaunchOption, setCustomLaunchOption] = useState<string>("") // 自定义启动项 // 从store读取配置数据
const [isResolutionEnabled, setIsResolutionEnabled] = useState<boolean>(true) // 是否启用分辨率和全屏设置 const testNote = fpsTest.state.config.testNote
const [resolutionWidth, setResolutionWidth] = useState<string>("") // 分辨率宽度 const customLaunchOption = fpsTest.state.config.customLaunchOption
const [resolutionHeight, setResolutionHeight] = useState<string>("") // 分辨率高度 const isResolutionEnabled = fpsTest.state.config.isResolutionEnabled
const [isFullscreen, setIsFullscreen] = useState<boolean>(true) // 全屏模式(默认全屏) const resolutionWidth = fpsTest.state.config.resolutionWidth
const [batchTestCount, setBatchTestCount] = useState<number>(1) // 批量测试次数1表示单次测试 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<{ const [batchTestProgress, setBatchTestProgress] = useState<{
current: number current: number
total: number total: number
@@ -253,6 +256,8 @@ export function FpsTest() {
const testStartTimeRef = useRef<number | null>(null) const testStartTimeRef = useRef<number | null>(null)
// 记录测试开始时的视频设置 // 记录测试开始时的视频设置
const testStartVideoSettingRef = useRef<typeof tool.state.videoSetting | null>(null) const testStartVideoSettingRef = useRef<typeof tool.state.videoSetting | null>(null)
// 记录当前测试的分辨率信息(用于备注)
const currentTestResolutionRef = useRef<{ width: string; height: string; label: string } | null>(null)
// 检测游戏是否运行 // 检测游戏是否运行
const checkGameRunning = useCallback(async () => { const checkGameRunning = useCallback(async () => {
@@ -277,14 +282,20 @@ export function FpsTest() {
return () => clearInterval(interval) return () => clearInterval(interval)
}, [checkGameRunning]) }, [checkGameRunning])
// 同步当前分辨率到状态(初始化时) // 同步当前分辨率到store(初始化时)
useEffect(() => { useEffect(() => {
if (tool.state.videoSetting) { if (tool.state.videoSetting) {
setResolutionWidth(tool.state.videoSetting.defaultres || "") if (!fpsTest.state.config.resolutionWidth && !fpsTest.state.config.resolutionHeight) {
setResolutionHeight(tool.state.videoSetting.defaultresheight || "") fpsTest.setResolution(
setIsFullscreen(tool.state.videoSetting.fullscreen === "1") tool.state.videoSetting.defaultres || "",
tool.state.videoSetting.defaultresheight || ""
)
} }
}, [tool.state.videoSetting]) if (fpsTest.state.config.isFullscreen === true && tool.state.videoSetting.fullscreen !== "1") {
fpsTest.setIsFullscreen(tool.state.videoSetting.fullscreen === "1")
}
}
}, [tool.state.videoSetting, fpsTest])
// 获取硬件信息 // 获取硬件信息
useEffect(() => { useEffect(() => {
@@ -322,6 +333,7 @@ export function FpsTest() {
testStartTimestampRef.current = null testStartTimestampRef.current = null
testStartTimeRef.current = null testStartTimeRef.current = null
testStartVideoSettingRef.current = null testStartVideoSettingRef.current = null
currentTestResolutionRef.current = null
// 如果启用了自动关闭游戏,则关闭游戏 // 如果启用了自动关闭游戏,则关闭游戏
if (tool.state.autoCloseGame) { if (tool.state.autoCloseGame) {
@@ -388,6 +400,11 @@ export function FpsTest() {
timeoutRef.current = null timeoutRef.current = null
} }
// 测试结束后读取视频设置(检测分辨率)
if (steam.state.steamDirValid && steam.currentUser()) {
await tool.getVideoConfig(steam.state.steamDir, steam.currentUser()?.steam_id32 || 0)
}
// 提取 avg 和 p1 值 // 提取 avg 和 p1 值
const { avg, p1 } = extractFpsMetrics(parsed.data) const { avg, p1 } = extractFpsMetrics(parsed.data)
@@ -396,20 +413,27 @@ export function FpsTest() {
const testDate = now.toISOString() const testDate = now.toISOString()
const mapConfig = BENCHMARK_MAPS[selectedMapIndex] const mapConfig = BENCHMARK_MAPS[selectedMapIndex]
// 使用测试开始时的视频设置(如果有的话),否则使用当前的 // 使用读取到的视频设置(测试结束后读取的)
const currentVideoSetting = const currentVideoSetting = tool.store.state.videoSetting
testStartVideoSettingRef.current || tool.store.state.videoSetting
// 如果是批量测试,保存结果到批量结果数组,否则直接保存 // 如果是批量测试,保存结果到批量结果数组,否则直接保存
const currentBatchProgress = batchTestProgress const currentBatchProgress = batchTestProgress
const currentResolution = currentTestResolutionRef.current
if (currentBatchProgress) { if (currentBatchProgress) {
const result = { avg, p1 } const result = { avg, p1 }
setBatchTestResults((prev) => [...prev, result]) setBatchTestResults((prev) => [...prev, result])
batchTestResultsRef.current = [...batchTestResultsRef.current, result] batchTestResultsRef.current = [...batchTestResultsRef.current, result]
// 添加带批量标识的备注 // 添加带批量标识和分辨率信息的备注
const batchNote = testNote let batchNote = ""
? `${testNote} [批量${currentBatchProgress.current}/${currentBatchProgress.total}]` if (currentResolution) {
batchNote = `[分辨率:${currentResolution.label}]`
}
if (testNote) {
batchNote = batchNote ? `${testNote} ${batchNote}` : testNote
}
batchNote = batchNote
? `${batchNote} [批量${currentBatchProgress.current}/${currentBatchProgress.total}]`
: `[批量${currentBatchProgress.current}/${currentBatchProgress.total}]` : `[批量${currentBatchProgress.current}/${currentBatchProgress.total}]`
fpsTest.addResult({ fpsTest.addResult({
@@ -445,6 +469,13 @@ export function FpsTest() {
// 不在这里处理由startTest的循环处理 // 不在这里处理由startTest的循环处理
} }
} else { } else {
// 单次测试,添加分辨率信息到备注
let singleNote = testNote
if (currentResolution) {
const resolutionNote = `[分辨率:${currentResolution.label}]`
singleNote = singleNote ? `${testNote} ${resolutionNote}` : resolutionNote
}
fpsTest.addResult({ fpsTest.addResult({
id: `${now.getTime()}-${Math.random().toString(36).slice(2, 11)}`, id: `${now.getTime()}-${Math.random().toString(36).slice(2, 11)}`,
testTime: parsed.timestamp, testTime: parsed.timestamp,
@@ -470,12 +501,13 @@ export function FpsTest() {
monitor: null, monitor: null,
} }
: null, : null,
note: testNote, // 保存备注 note: singleNote, // 保存备注(包含分辨率信息)
}) })
} }
// 清除保存的启动时视频设置 // 清除保存的启动时视频设置和分辨率信息
testStartVideoSettingRef.current = null testStartVideoSettingRef.current = null
currentTestResolutionRef.current = null
if (!silent) { if (!silent) {
if (avg !== null || p1 !== null) { if (avg !== null || p1 !== null) {
@@ -530,6 +562,7 @@ export function FpsTest() {
tool.state.autoCloseGame, tool.state.autoCloseGame,
batchTestProgress, batchTestProgress,
testNote, testNote,
batchTestResults,
] ]
) )
@@ -557,6 +590,7 @@ export function FpsTest() {
testStartTimestampRef.current = null testStartTimestampRef.current = null
testStartTimeRef.current = null testStartTimeRef.current = null
testStartVideoSettingRef.current = null testStartVideoSettingRef.current = null
currentTestResolutionRef.current = null
addToast({ addToast({
title: "测试超时200秒测试失败", title: "测试超时200秒测试失败",
variant: "flat", variant: "flat",
@@ -599,7 +633,8 @@ export function FpsTest() {
const runSingleTest = async ( const runSingleTest = async (
testIndex: number, testIndex: number,
totalTests: number, totalTests: number,
isFirstTest: boolean = false isFirstTest: boolean = false,
resolution?: { width: string; height: string; label: string }
): Promise<boolean> => { ): Promise<boolean> => {
if (!steam.state.steamDir || !steam.state.cs2Dir) { if (!steam.state.steamDir || !steam.state.cs2Dir) {
return false return false
@@ -642,11 +677,18 @@ export function FpsTest() {
// 构建启动参数:基础参数 + 分辨率和全屏设置 + 自定义启动项(如果有) // 构建启动参数:基础参数 + 分辨率和全屏设置 + 自定义启动项(如果有)
let baseLaunchOption = `-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}`
// 使用传入的分辨率如果没有则使用store中的分辨率
const currentResolution = resolution || {
width: resolutionWidth,
height: resolutionHeight,
label: `${resolutionWidth}x${resolutionHeight}`,
}
// 只有在启用分辨率和全屏设置时才添加相关参数 // 只有在启用分辨率和全屏设置时才添加相关参数
if (isResolutionEnabled) { if (isResolutionEnabled) {
// 添加分辨率设置(如果有设置) // 添加分辨率设置(如果有设置)
if (resolutionWidth && resolutionHeight) { if (currentResolution.width && currentResolution.height) {
baseLaunchOption += ` -w ${resolutionWidth} -h ${resolutionHeight}` baseLaunchOption += ` -w ${currentResolution.width} -h ${currentResolution.height}`
} }
// 添加全屏/窗口化设置 // 添加全屏/窗口化设置
@@ -669,29 +711,17 @@ export function FpsTest() {
server: "worldwide", server: "worldwide",
}) })
// 延迟读取视频设置修复分辨率读取bug // 视频设置将在测试结束后读取在readResult中
// 等待游戏启动并应用分辨率设置5秒 // 保存当前测试的分辨率信息
// 批量测试的第一次不需要等待,因为游戏还没有启动过 currentTestResolutionRef.current = currentResolution
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 }
const resolutionInfo = currentResolution ? ` (${currentResolution.label})` : ""
if (totalTests > 1) { if (totalTests > 1) {
addToast({ addToast({
title: `批量测试 ${testIndex}/${totalTests}:已启动 ${mapConfig.label} 测试,正在自动监听结果...`, title: `批量测试 ${testIndex}/${totalTests}${resolutionInfo}:已启动 ${mapConfig.label} 测试,正在自动监听结果...`,
}) })
} else { } else {
addToast({ title: `已启动 ${mapConfig.label} 测试,正在自动监听结果...` }) addToast({ title: `已启动 ${mapConfig.label} 测试${resolutionInfo},正在自动监听结果...` })
} }
// 开始自动监听文件更新 // 开始自动监听文件更新
@@ -730,6 +760,7 @@ export function FpsTest() {
testStartTimestampRef.current = null testStartTimestampRef.current = null
testStartTimeRef.current = null testStartTimeRef.current = null
testStartVideoSettingRef.current = null testStartVideoSettingRef.current = null
currentTestResolutionRef.current = null
return false return false
} }
} }
@@ -746,14 +777,140 @@ export function FpsTest() {
return return
} }
// 检查是否启用分辨率组
if (isResolutionGroupEnabled && resolutionGroup.length === 0) {
addToast({ title: "请先添加分辨率到分辨率组", variant: "flat", color: "warning" })
return
}
const totalTests = batchTestCount const totalTests = batchTestCount
batchTestAbortRef.current = false batchTestAbortRef.current = false
setBatchTestResults([]) setBatchTestResults([])
batchTestResultsRef.current = [] batchTestResultsRef.current = []
if (totalTests > 1) { // 如果启用了分辨率组,对每个分辨率进行批量测试
// 批量测试 if (isResolutionGroupEnabled && resolutionGroup.length > 0) {
setIsBatchTesting(true) // 设置批量测试状态 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 }) setBatchTestProgress({ current: 0, total: totalTests })
for (let i = 1; i <= totalTests; i++) { for (let i = 1; i <= totalTests; i++) {
@@ -773,9 +930,7 @@ export function FpsTest() {
// 如果不是最后一次测试等待5秒再继续 // 如果不是最后一次测试等待5秒再继续
if (i < totalTests && !batchTestAbortRef.current) { if (i < totalTests && !batchTestAbortRef.current) {
// 确保游戏已关闭如果自动关闭已启用readResult中已经关闭了
if (tool.state.autoCloseGame) { if (tool.state.autoCloseGame) {
// 等待游戏关闭完成readResult中延迟2秒关闭
await new Promise((resolve) => setTimeout(resolve, 3000)) await new Promise((resolve) => setTimeout(resolve, 3000))
try { try {
await invoke("kill_game").catch(() => {}) await invoke("kill_game").catch(() => {})
@@ -783,13 +938,11 @@ export function FpsTest() {
console.error("关闭游戏失败:", error) console.error("关闭游戏失败:", error)
} }
} }
// 等待5秒
await new Promise((resolve) => setTimeout(resolve, 5000)) await new Promise((resolve) => setTimeout(resolve, 5000))
} }
} }
// 批量测试完成,计算平均值 // 批量测试完成,计算平均值
// 使用ref中收集的结果确保获取到所有结果
const finalResults = batchTestResultsRef.current const finalResults = batchTestResultsRef.current
if (!batchTestAbortRef.current && finalResults.length > 0) { if (!batchTestAbortRef.current && finalResults.length > 0) {
const validResults = finalResults.filter((r) => r.avg !== null || r.p1 !== null) const validResults = finalResults.filter((r) => r.avg !== null || r.p1 !== null)
@@ -850,7 +1003,7 @@ export function FpsTest() {
setBatchTestProgress(null) setBatchTestProgress(null)
setBatchTestResults([]) setBatchTestResults([])
setIsBatchTesting(false) // 批量测试结束,重置状态 setIsBatchTesting(false)
} else { } else {
// 单次测试 // 单次测试
await runSingleTest(1, 1, true) 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 }) => { const handlePresetResolution = (preset: { width: string; height: string; label: string }) => {
void handleSetResolution(preset.width, preset.height) fpsTest.setResolution(preset.width, preset.height)
} }
return ( return (
@@ -1198,7 +1340,7 @@ export function FpsTest() {
} }
}} }}
aria-label="测试地图选择" aria-label="测试地图选择"
size="md" size="sm"
radius="lg" radius="lg"
> >
{BENCHMARK_MAPS.map((map, index) => ( {BENCHMARK_MAPS.map((map, index) => (
@@ -1215,7 +1357,7 @@ export function FpsTest() {
onSelectionChange={(keys) => { onSelectionChange={(keys) => {
const value = Array.from(keys)[0] const value = Array.from(keys)[0]
if (value && !isMonitoring) { if (value && !isMonitoring) {
setBatchTestCount(Number(value)) fpsTest.setBatchTestCount(Number(value))
} }
}} }}
isDisabled={isMonitoring} isDisabled={isMonitoring}
@@ -1237,7 +1379,7 @@ export function FpsTest() {
size="md" size="md"
placeholder="输入测试备注" placeholder="输入测试备注"
value={testNote} value={testNote}
onValueChange={setTestNote} onValueChange={fpsTest.setTestNote}
isDisabled={isMonitoring} isDisabled={isMonitoring}
className="flex-1" className="flex-1"
/> />
@@ -1245,6 +1387,7 @@ export function FpsTest() {
</div> </div>
</div> </div>
{/* 启动项占满一行,右侧放置分辨率和全屏切换 */} {/* 启动项占满一行,右侧放置分辨率和全屏切换 */}
<div className="flex items-start gap-4"> <div className="flex items-start gap-4">
{/* 自定义启动项 */} {/* 自定义启动项 */}
@@ -1255,7 +1398,7 @@ export function FpsTest() {
size="md" size="md"
placeholder="输入自定义启动参数(可选)" placeholder="输入自定义启动参数(可选)"
value={customLaunchOption} value={customLaunchOption}
onValueChange={setCustomLaunchOption} onValueChange={fpsTest.setCustomLaunchOption}
isDisabled={isMonitoring} isDisabled={isMonitoring}
className="flex-1" className="flex-1"
/> />
@@ -1266,8 +1409,32 @@ export function FpsTest() {
<div className="flex items-end gap-2 shrink-0"> <div className="flex items-end gap-2 shrink-0">
{/* 分辨率设置 */} {/* 分辨率设置 */}
<div className="flex flex-col gap-1.5"> <div className="flex flex-col gap-1.5">
<div className="flex items-center gap-1"> {/* 工具栏:分辨率标签 + 按钮 */}
<label className="text-xs text-default-500"></label> <div className="flex items-center gap-2">
<label className="text-xs text-default-500 shrink-0"></label>
<div className="flex flex-wrap items-center gap-1">
<Button
size="sm"
variant={isResolutionGroupEnabled ? "solid" : "flat"}
color={isResolutionGroupEnabled ? "primary" : "default"}
onPress={() => {
if (!isMonitoring) {
const newValue = !isResolutionGroupEnabled
fpsTest.setIsResolutionGroupEnabled(newValue)
// 启用分辨率组时,自动启用分辨率功能
if (newValue && !isResolutionEnabled) {
fpsTest.setIsResolutionEnabled(true)
}
}
}}
isDisabled={isMonitoring}
className="h-5 min-w-[40px] px-1.5 text-xs font-medium"
>
<List size={14} />
</Button>
{!isResolutionGroupEnabled && (
<>
<Dropdown placement="bottom-end" className="min-w-fit"> <Dropdown placement="bottom-end" className="min-w-fit">
<DropdownTrigger> <DropdownTrigger>
<Button <Button
@@ -1296,54 +1463,97 @@ export function FpsTest() {
size="sm" size="sm"
variant={isResolutionEnabled ? "solid" : "flat"} variant={isResolutionEnabled ? "solid" : "flat"}
color={isResolutionEnabled ? "primary" : "default"} color={isResolutionEnabled ? "primary" : "default"}
onPress={() => setIsResolutionEnabled(!isResolutionEnabled)} onPress={() => fpsTest.setIsResolutionEnabled(!isResolutionEnabled)}
isDisabled={isMonitoring} isDisabled={isMonitoring}
className="h-5 min-w-[40px] px-1.5 text-xs font-medium" className="h-5 min-w-[40px] px-1.5 text-xs font-medium"
> >
{isResolutionEnabled ? "启用" : "关闭"} {isResolutionEnabled ? "启用" : "关闭"}
</Button> </Button>
</>
)}
{isResolutionGroupEnabled && (
<>
<Dropdown placement="bottom-end" className="min-w-fit">
<DropdownTrigger>
<Button
size="sm"
variant="flat"
className="h-5 min-w-[40px] px-1.5 text-xs"
isDisabled={isMonitoring}
>
</Button>
</DropdownTrigger>
<DropdownMenu aria-label="预设分辨率" className="">
{PRESET_RESOLUTIONS.map(
(preset: { width: string; height: string; label: string }) => (
<DropdownItem
key={preset.label}
onPress={() => {
if (!isMonitoring) {
fpsTest.addResolutionToGroup({
width: preset.width,
height: preset.height,
label: preset.label,
})
}
}}
>
{preset.label}
</DropdownItem>
)
)}
</DropdownMenu>
</Dropdown>
<Button
size="sm"
variant="flat"
onPress={() => {
if (!isMonitoring && resolutionWidth && resolutionHeight) {
fpsTest.addResolutionToGroup({
width: resolutionWidth,
height: resolutionHeight,
label: `${resolutionWidth}x${resolutionHeight}`,
})
}
}}
isDisabled={isMonitoring || !resolutionWidth || !resolutionHeight}
className="h-5 min-w-[40px] px-1.5 text-xs"
>
</Button>
</>
)}
</div> </div>
</div>
{/* 主体:宽高输入框 + 全屏按钮(始终显示) */}
<div className="flex items-center gap-2">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<Input <Input
size="md" size="sm"
type="number" type="number"
placeholder="宽" placeholder="宽"
value={resolutionWidth} value={resolutionWidth}
onValueChange={setResolutionWidth} onValueChange={(val) => fpsTest.setResolution(val, resolutionHeight)}
isDisabled={!isResolutionEnabled || isMonitoring} isDisabled={isResolutionGroupEnabled ? isMonitoring : (!isResolutionEnabled || isMonitoring)}
className="w-20" className="w-20"
onBlur={() => {
if (resolutionWidth && resolutionHeight) {
void handleSetResolution(resolutionWidth, resolutionHeight)
}
}}
/> />
<span className="text-xs text-default-400">x</span> <span className="text-xs text-default-400">x</span>
<Input <Input
size="md" size="sm"
type="number" type="number"
placeholder="高" placeholder="高"
value={resolutionHeight} value={resolutionHeight}
onValueChange={setResolutionHeight} onValueChange={(val) => fpsTest.setResolution(resolutionWidth, val)}
isDisabled={!isResolutionEnabled || isMonitoring} isDisabled={isResolutionGroupEnabled ? isMonitoring : (!isResolutionEnabled || isMonitoring)}
className="w-20" className="w-20"
onBlur={() => {
if (resolutionWidth && resolutionHeight) {
void handleSetResolution(resolutionWidth, resolutionHeight)
}
}}
/> />
</div> </div>
</div>
{/* 全屏/窗口化切换 */}
<div className="flex flex-col gap-1.5">
<div className="h-5" /> {/* 对齐标签高度 */}
<Button <Button
size="md" size="sm"
variant={isFullscreen ? "solid" : "flat"} variant={isFullscreen ? "solid" : "flat"}
color={isFullscreen ? "primary" : "default"} color={isFullscreen ? "primary" : "default"}
onPress={handleToggleFullscreen} onPress={() => fpsTest.setIsFullscreen(!isFullscreen)}
isDisabled={!isResolutionEnabled || isMonitoring} isDisabled={!isResolutionEnabled || isMonitoring}
className="font-medium" className="font-medium"
> >
@@ -1352,6 +1562,7 @@ export function FpsTest() {
</div> </div>
</div> </div>
</div> </div>
</div>
{/* 工具栏:按钮靠右对齐 */} {/* 工具栏:按钮靠右对齐 */}
<div className="flex items-center justify-start gap-2"> <div className="flex items-center justify-start gap-2">
@@ -1404,6 +1615,30 @@ export function FpsTest() {
</Button> </Button>
{isResolutionGroupEnabled && (
<div className="flex flex-wrap items-center gap-2">
{resolutionGroup.map((res, index) => (
<Chip
key={index}
size="sm"
variant="flat"
color="primary"
onClose={() => {
if (!isMonitoring) {
fpsTest.removeResolutionFromGroup(index)
}
}}
isCloseable={!isMonitoring}
>
{res.label}
</Chip>
))}
{resolutionGroup.length === 0 && (
<span className="text-xs text-default-400"></span>
)}
</div>
)}
{isMonitoring && ( {isMonitoring && (
<Chip size="lg" color="primary" variant="flat" className="text-xs"> <Chip size="lg" color="primary" variant="flat" className="text-xs">
... ...
@@ -1482,3 +1717,4 @@ export function FpsTest() {
</Card> </Card>
) )
} }

View File

@@ -25,8 +25,26 @@ export interface FpsTestResult {
note?: string // 备注(可选,用于向后兼容) note?: string // 备注(可选,用于向后兼容)
} }
export interface ResolutionGroupItem {
width: string
height: string
label: string
}
const defaultValue = { const defaultValue = {
results: [] as FpsTestResult[], results: [] as FpsTestResult[],
// FpsTest配置数据
config: {
batchTestCount: 1, // 批量测试次数
isResolutionGroupEnabled: false, // 是否启用分辨率组
resolutionGroup: [] as ResolutionGroupItem[], // 分辨率组列表
testNote: "", // 测试备注
customLaunchOption: "", // 自定义启动项
isResolutionEnabled: true, // 是否启用分辨率和全屏设置
resolutionWidth: "", // 分辨率宽度
resolutionHeight: "", // 分辨率高度
isFullscreen: true, // 全屏模式
},
} }
export const fpsTestStore = store( export const fpsTestStore = store(
@@ -46,6 +64,17 @@ export const useFpsTestStore = () => {
removeResult, removeResult,
clearResults, clearResults,
updateNote, 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
}