ScreenLockDetector/docs/FPS_CALCULATION.md

253 lines
6.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# FPS 计算实现文档
## 概述
本文档描述了如何在 ScreenLockDetector 项目中实现真实的 FPS每秒帧数计算功能。
## 问题背景
之前的实现中FPS 显示为硬编码的 "FPS: ~60",无法反映真实的渲染性能。
## 解决方案
采用 **移动平均窗口法Moving Average Window** 来计算实时 FPS。
### 核心算法
1. **记录帧时间间隔**:每渲染一帧,记录与上一帧的时间间隔(微秒)
2. **维护循环缓冲区**:保存最近 N 帧的时间间隔数据
3. **计算平均 FPS**
- 计算所有样本的平均帧时间
- 转换为 FPS`FPS = 1,000,000 / 平均帧时间(微秒)`
### 算法优点
-**平滑输出**:使用多帧平均,避免 FPS 值剧烈跳动
-**实时响应**样本窗口足够小60帧能快速反映性能变化
-**精确度高**:使用微秒级时间戳,计算精度高
-**低开销**:只需简单的加法和除法运算
## 实现细节
### 1. VulkanWidget 实现
#### 添加成员变量vulkanwidget.h
```cpp
// FPS 计算
QDateTime m_lastFrameTime; // 上一帧的时间戳
double m_currentFps; // 当前计算出的 FPS 值
std::vector<qint64> m_frameTimes; // 存储最近帧的时间间隔(微秒)
static const int FPS_SAMPLE_COUNT = 60; // 样本数量使用最近60帧
```
#### 初始化vulkanwidget.cpp 构造函数)
```cpp
, m_lastFrameTime(QDateTime::currentDateTime())
, m_currentFps(0.0)
{
// ...
// Initialize FPS calculation
m_frameTimes.reserve(FPS_SAMPLE_COUNT);
}
```
#### FPS 计算逻辑renderFrame() 函数)
```cpp
// Calculate FPS
QDateTime currentTime = QDateTime::currentDateTime();
qint64 frameTimeUs = m_lastFrameTime.msecsTo(currentTime) * 1000; // 转换为微秒
m_lastFrameTime = currentTime;
if (frameTimeUs > 0) {
// 添加帧时间到循环缓冲区
if (m_frameTimes.size() >= FPS_SAMPLE_COUNT) {
m_frameTimes.erase(m_frameTimes.begin()); // 移除最旧的样本
}
m_frameTimes.push_back(frameTimeUs);
// 计算平均 FPS
if (!m_frameTimes.empty()) {
qint64 totalTimeUs = 0;
for (qint64 time : m_frameTimes) {
totalTimeUs += time;
}
double avgFrameTimeUs = static_cast<double>(totalTimeUs) / m_frameTimes.size();
m_currentFps = 1000000.0 / avgFrameTimeUs; // 微秒转 FPS
}
}
```
#### 传递 FPS 给渲染器
```cpp
m_renderer->recordCommandBuffer(commandBuffer, imageIndex, imageView,
m_frameCount,
static_cast<double>(elapsedTime),
m_currentFps, // 传递实时 FPS
m_rotationAngle,
m_wavePhase,
m_renderingEnabled,
lockInfo.toStdString());
```
### 2. VulkanRenderer 实现
#### 更新函数签名vulkanrenderer.h
```cpp
void recordCommandBuffer(VkCommandBuffer commandBuffer,
uint32_t imageIndex,
VkImageView imageView,
int frameCount,
double elapsedTime,
double fps, // 新增参数
double rotationAngle,
double wavePhase,
bool paintingEnabled,
const std::string& lockInfo);
```
#### 使用 FPS 显示vulkanrenderer.cpp
```cpp
// 之前:硬编码
std::string fpsStr = "FPS: ~60";
// 现在:使用真实值
std::string fpsStr = "FPS: " + std::to_string((int)fps);
```
### 3. CustomWidget 实现
类似的实现方式,在 `paintEvent()` 中计算 FPS
```cpp
// Calculate FPS
QDateTime currentTime = QDateTime::currentDateTime();
qint64 frameTimeUs = m_lastFrameTime.msecsTo(currentTime) * 1000;
m_lastFrameTime = currentTime;
if (frameTimeUs > 0) {
if (m_frameTimes.size() >= FPS_SAMPLE_COUNT) {
m_frameTimes.erase(m_frameTimes.begin());
}
m_frameTimes.push_back(frameTimeUs);
if (!m_frameTimes.empty()) {
qint64 totalTimeUs = 0;
for (qint64 time : m_frameTimes) {
totalTimeUs += time;
}
double avgFrameTimeUs = static_cast<double>(totalTimeUs) / m_frameTimes.size();
m_currentFps = 1000000.0 / avgFrameTimeUs;
}
}
```
显示时使用真实值:
```cpp
QString stats = QString(
"Frame Count: %1\n"
"FPS: %2\n"
"Rotation: %3°\n"
"Running Time: %4s"
).arg(m_frameCount)
.arg(static_cast<int>(m_currentFps)) // 显示为整数
.arg(static_cast<int>(m_rotationAngle))
.arg(elapsed);
```
## 性能考虑
### 内存占用
- 每个样本8 字节qint64
- 总内存60 × 8 = 480 字节(可忽略不计)
### CPU 开销
- 每帧操作:
- 1 次时间戳获取:~100 纳秒
- 1 次数组操作(插入/删除O(n) = O(60)
- 1 次求和计算O(60)
- 总开销:< 1 微秒可忽略不计
### 优化建议
如果需要进一步优化性能可以考虑
1. **使用循环队列**避免 `erase()` 操作
```cpp
std::array<qint64, FPS_SAMPLE_COUNT> m_frameTimes;
size_t m_frameTimeIndex = 0;
```
2. **增量更新总和**:避免每次重新计算
```cpp
qint64 m_totalFrameTime = 0;
// 更新时:
m_totalFrameTime -= m_frameTimes[oldestIndex];
m_totalFrameTime += newFrameTime;
```
3. **降低更新频率**:每 N 帧更新一次 FPS 显示
## 使用方法
### 获取 FPS 值
```cpp
// VulkanWidget
double fps = vulkanWidget->getCurrentFps();
// CustomWidget
double fps = customWidget->getCurrentFps();
```
### 配置样本数量
修改 `FPS_SAMPLE_COUNT` 常量:
- **较小值30**:更快响应性能变化,但可能不够平滑
- **较大值120**:更平滑的显示,但响应较慢
- **推荐值60**:平衡响应速度和平滑度
## 测试验证
### 预期结果
1. **正常运行时**FPS 应接近 60受 VSync 限制)
2. **性能下降时**FPS 会相应降低(如窗口最小化、系统负载高)
3. **锁屏时**FPS 应降为 0 或接近 0
### 调试输出
可以添加调试日志查看详细信息:
```cpp
qDebug() << "Frame time:" << frameTimeUs << "us, FPS:" << m_currentFps;
```
## 相关文件
- `src/vulkanwidget.h` - VulkanWidget 头文件
- `src/vulkanwidget.cpp` - VulkanWidget 实现
- `src/vulkanrenderer.h` - VulkanRenderer 头文件
- `src/vulkanrenderer.cpp` - VulkanRenderer 实现
- `src/customwidget.h` - CustomWidget 头文件
- `src/customwidget.cpp` - CustomWidget 实现
## 总结
通过移动平均窗口法,我们实现了:
**真实的 FPS 显示**:反映实际渲染性能
**平滑的数值变化**:避免抖动
**低性能开销**:对渲染性能影响可忽略
**易于扩展**:可以轻松调整样本数量和计算方式
这个实现为性能监控和调优提供了重要的参考数据。