157 lines
2.8 KiB
Markdown
157 lines
2.8 KiB
Markdown
# startRender(int fps) 作用流程 FAQ
|
||
|
||
## startRender(int fps) 做了什么
|
||
|
||
`startRender(int refreshTimesPreSecond)` 只做两件事:
|
||
|
||
```text
|
||
保存 mRefreshTimesPreSecond
|
||
调用 startRender()
|
||
```
|
||
|
||
对应代码流程:
|
||
|
||
```text
|
||
Plot::startRender(int fps)
|
||
↓
|
||
d->mRefreshTimesPreSecond = fps
|
||
↓
|
||
Plot::startRender()
|
||
```
|
||
|
||
## startRender() 做了什么
|
||
|
||
`startRender()` 的作用是启动 Plot 的渲染调度。
|
||
|
||
流程:
|
||
|
||
```text
|
||
检查 Plot 是否正在析构
|
||
↓
|
||
mRenderEnabled = true
|
||
↓
|
||
投递任务到 asio scheduler
|
||
↓
|
||
取消旧 mRenderTimer
|
||
↓
|
||
scheduleRenderTimer()
|
||
↓
|
||
submitRender()
|
||
```
|
||
|
||
它会立即触发一次 `submitRender()`,不是等下一个 timer 周期。
|
||
|
||
## fps 参数现在是不是严格帧率上限
|
||
|
||
不是。
|
||
|
||
当前 `fps` 只决定 asio timer 的周期:
|
||
|
||
```text
|
||
interval = round(1000.0 / fps)
|
||
```
|
||
|
||
timer 到期后:
|
||
|
||
```text
|
||
submitRender()
|
||
scheduleRenderTimer()
|
||
```
|
||
|
||
所以它的真实语义是周期性 heartbeat / 兜底触发,不是硬性 FPS cap。
|
||
|
||
## 为什么不是严格限帧
|
||
|
||
因为当前渲染是事件驱动为主。
|
||
|
||
除了 timer 会触发 `submitRender()`,下面这些路径也会立即触发:
|
||
|
||
```text
|
||
RenderAble::markStateDirty()
|
||
↓
|
||
Plot::markRenderStateDirty()
|
||
↓
|
||
requestRender()
|
||
↓
|
||
submitRender()
|
||
```
|
||
|
||
```text
|
||
RenderData::markInputDirty()
|
||
↓
|
||
RenderAble::markRenderDirty()
|
||
↓
|
||
requestRender()
|
||
↓
|
||
submitRender()
|
||
```
|
||
|
||
```text
|
||
finishRender()
|
||
↓
|
||
pipeline.hasNewState()
|
||
↓
|
||
submitRender()
|
||
```
|
||
|
||
只要 State/Input 有新版本,系统可以绕过 timer 周期继续推进下一帧。
|
||
|
||
## asio 里面还有没有定时器
|
||
|
||
有。
|
||
|
||
相关成员和函数:
|
||
|
||
```text
|
||
PlotPrivate::mRenderTimer
|
||
PlotPrivate::mRefreshTimesPreSecond
|
||
Plot::scheduleRenderTimer()
|
||
RenderScheduler::initializePlotOnScheduler()
|
||
```
|
||
|
||
`RenderScheduler::initializePlotOnScheduler()` 为每个 Plot 创建 `asio::steady_timer`。
|
||
|
||
`scheduleRenderTimer()` 使用 `mRefreshTimesPreSecond` 计算间隔并注册 `async_wait`。
|
||
|
||
## timer 在当前架构里还有没有必要
|
||
|
||
当前仍然有必要保留。
|
||
|
||
原因:
|
||
|
||
```text
|
||
保持 startRender(int) 旧调用语义
|
||
保证没有显式 dirty 事件时仍有周期性检查
|
||
作为异常丢事件后的兜底触发
|
||
给性能测试框提供一个稳定的周期参考
|
||
```
|
||
|
||
但它不是渲染正确性的唯一入口。
|
||
|
||
当前正确性主要依赖:
|
||
|
||
```text
|
||
State dirty
|
||
Input dirty
|
||
finishRender 后续版本检查
|
||
```
|
||
|
||
## 如果以后要严格限帧应该怎么做
|
||
|
||
应该新增独立调度策略,而不是继续复用 `startRender(int)` 的现有语义。
|
||
|
||
建议拆成:
|
||
|
||
```text
|
||
Immediate:
|
||
dirty 后立即 requestRender()
|
||
|
||
FixedRate:
|
||
dirty 后只标记 pending,由 timer 周期统一 submitRender()
|
||
|
||
Throttle:
|
||
dirty 后允许立即渲染,但两帧之间必须满足最小间隔
|
||
```
|
||
|
||
这样 `startRender(int)` 才能变成明确的帧率策略参数。
|