# 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)` 才能变成明确的帧率策略参数。
