# 图表渲染整体架构设计

## 1. 总体模型

每个 RenderAble 拆成四类数据：

```text
State 三缓冲
Input 三缓冲
Render 私有缓存
Color 三缓冲
```

职责边界：

```text
State：
    小型、可复制、表示显示配置和稳定状态。

Input：
    外部输入到渲染线程的增量数据。

RenderCache：
    渲染侧长期缓存，不复制，不参与状态交换。

Color：
    后台生成的像素图，交给 Qt 主线程贴图。
```

核心原则：

```text
配置走 State。
样本走 Input。
缓存留在 RenderData 私有成员。
像素走 Color。
```

不能把流式样本、环形缓冲、累计矩阵塞进 State。

State 的同步语义是复制。

Input 的同步语义是角色交换。

RenderCache 的语义是渲染侧所有权。

## 2. 整体流程

```text
用户操作 / 属性修改
    ↓
修改 edit_state
    ↓
state edit -> ready 复制
    ↓
state ready -> render 交换
```

```text
数据输入 / giveData
    ↓
追加到 edit_input
    ↓
input edit -> ready 交换
    ↓
input ready -> render 交换
    ↓
prepareData 消费 render_input
    ↓
更新 RenderCache
    ↓
clear render_input
```

```text
draw
    ↓
读取 render_state
    ↓
读取 RenderCache
    ↓
写 render_color
    ↓
color render -> ready 交换
    ↓
Qt paintEvent 消费 ready_color 到 front_color
```

完整数据链路：

```text
edit_state + edit_input
    ↓
prepareData
render_state + render_input
    ↓
RenderCache
    ↓
render_color
    ↓
ready_color
    ↓
front_color
```

## 3. 三缓冲控制器

State、Input、Color 的角色交换都使用同一个控制器：

```text
Triple_Role_Buffer_Control
```

控制器只负责：

```text
role -> physical index
busy 标记
lease
swap_role
```

它不理解实际数据类型。

实际数据可以是：

```text
RenderState
InputData
ColorBuffer
```

控制器语义：

```text
slot_state 管角色映射。
busy 管物理占用。
lease 管一次具体使用。
swap_role 只交换角色，不复制业务数据。
```

长时间使用某个角色时，必须释放 lease，不允许按 role 释放。

因为使用期间 role 可能已经被交换。

## 4. State 三缓冲

State 有三个角色：

```text
State_Edit
State_Ready
State_Render
```

State 里只放：

```text
坐标轴指针
范围
颜色
画笔
字体
hover 状态
marker 状态
模式开关
点数配置
```

State 里不放：

```text
输入样本列表
环形缓冲区
QImage
累计矩阵
曲线当前数据缓存
WaterFall 行缓存
Afterglow 能量累计缓存
```

State 同步流程：

```text
如果 edit_state.version 新于 ready_state.version：
    mark ready_state
    copy edit_state -> ready_state
    unmark ready_state

如果 ready_state.version 新于 render_state.version：
    swap ready_state, render_state
```

State 的核心意义：

```text
把可复制配置从编辑侧稳定发布给渲染侧。
跳过中间状态。
保证 CPU 绘制时读取稳定 render_state。
```

## 5. Input 三缓冲

Input 有三个角色：

```text
Input_Edit
Input_Ready
Input_Render
```

InputData 里放增量输入：

```text
WaterFall 新行
AudioFrequent 新功率点
TimeAxis 新时间点
SweepFrequent 新块数据
Planisphere 新星座点
Spectrum 新曲线样本
Afterglow 新功率行
```

输入线程只追加到 edit_input。

追加成功后：

```text
input_slot.version = ++mInputVersion
input_slot.pending = true
requestRender()
```

Input 同步流程：

```text
如果 edit_input.pending 且 edit_input.version 新于 ready_input.version：
    swap edit_input, ready_input

如果 ready_input.pending 且 ready_input.version 新于 render_input.version：
    swap ready_input, render_input
```

prepareData 消费流程：

```text
auto input = renderInputData()
for each delta in input:
    更新 RenderCache
clearRenderInput()
```

clearRenderInput 必须同时完成：

```text
清空 render_input 数据
render_input.pending = false
```

Input 三缓冲的核心意义：

```text
输入数据不复制到 State。
输入数据不触发 RenderState 大对象复制。
渲染侧按批消费增量。
edit_input 可以在下一轮继续接收新数据。
```

## 6. Render 私有缓存

RenderCache 是 RenderData 的私有成员，不参与三缓冲复制。

典型对象：

```text
QImage image
WaterFallRingBuffer ringBuffer
TimeAxis::Psc::StreamRingBuffer_ST buffer
AudioFrequent::Psc::StreamRingBuffer_ST buffer
SweepFrequent::mFrequents
Planisphere::dataList
Afterglow cachedPowerData / oldCachePowerData / mergedPowerData
Spectrum curPowers / maxPowers / minPowers / frequents
```

RenderCache 的来源：

```text
render_state 配置
render_input 增量
```

RenderCache 的更新位置：

```text
prepareData()
```

RenderCache 的读取位置：

```text
draw()
```

RenderCache 不应该由属性 setter 直接修改。

属性 setter 只改 edit_state。

数据输入函数只追加 edit_input。

RenderAble 可以选择开启独立像素缓存：

```text
RenderAble::setIndependentPixelCache(true)
```

开启后：

```text
该 RenderAble 的 markRenderDirty 不再触发整张 Plot renderColor。
该 RenderAble 单独 prepare/draw 到自己的透明 QImage。
Plot::paintEvent 在绘制 Plot 级 ColorBuffer 后叠加独立像素缓存。
```

默认不开启。

它适合：

```text
PerformanceShower
轻量 overlay
高频变化但不应该触发整图重绘的辅助层
```

## 7. Color 三缓冲

Color 有三个角色：

```text
Color_Front
Color_Ready
Color_Render
```

Color_Render：

```text
CPU 后台线程写入。
```

Color_Ready：

```text
后台完成后等待 Qt 主线程消费。
```

Color_Front：

```text
Qt paintEvent 当前显示。
```

发布流程：

```text
render_color.version 新于 ready_color.version：
    swap ready_color, render_color

ready_color.version 新于 front_color.version：
    request update
```

paintEvent 流程：

```text
按 Plot_Buffer_Acquire_Mode 消费 ready_color
mark front_color
drawImage(front_color)
unmark front_color
```

Plot_Buffer_Acquire_Mode 有两种策略：

```text
Try：
    paintEvent 尝试交换 ready_color -> front_color。
    如果物理 buffer 正忙，立即放弃本次新帧消费。
    这是默认模式，GUI 线程不进入 atomic_wait。

Wait：
    paintEvent 等待参与交换或 front_color lease 可用。
    只适合调用方明确接受 GUI 线程短暂等待的场景。
```

Color 发布侧仍然可以等待 Color_Render / Color_Ready，因为它运行在调度和后台渲染流程，不阻塞 Qt paintEvent。

## 8. 三缓冲获取策略

三缓冲获取策略不是单一全局开关，而是分层控制。

Plot 层：

```text
Plot_Buffer_Acquire_Mode
```

只控制 Qt paintEvent 消费 Color_Ready / Color_Front 的行为。

默认：

```text
Try
```

RenderAble 层：

```text
Plot_Buffer_Acquire_Mode
```

控制单个 RenderAble 在后台 draw 阶段获取 State_Render lease 的行为。

默认：

```text
Wait
```

RenderAble 设置为 Try 后：

```text
draw 阶段取不到 State_Render lease
    ↓
跳过该 RenderAble 本帧绘制
```

State/Input 的编辑发布路径默认仍使用 Wait。

原因：

```text
setter / giveData 当前多数是 void 语义。
Try 失败后没有统一返回值表达本次修改是否被接受。
在没有重新设计 API 返回语义前，不允许静默丢弃编辑和输入。
```

Plot 还保存一个 RenderAble 默认获取策略：

```text
Plot::renderAbleBufferAcquireMode()
Plot::setRenderAbleBufferAcquireMode()
```

RenderAble init 到 Plot 时会复制父 Plot 当前的默认策略。

之后单个 RenderAble 可以独立调用：

```text
RenderAble::bufferAcquireMode()
RenderAble::setBufferAcquireMode()
```

## 9. 调度版本

渲染调度使用 RenderPipeline 的帧版本。

State dirty 和 Input dirty 都会推进帧版本。

区别：

```text
State dirty：
    edit_state.version++
    frame_version++

Input dirty：
    input_version++
    frame_version++
```

Input dirty 不递增 edit_state.version。

这点非常重要。

它保证流式输入不会导致 RenderState 全量复制。

## 10. RenderScheduler

渲染调度使用一个全局 RenderScheduler。

RenderScheduler 由两部分组成：

```text
单线程 asio::io_context
asio::thread_pool
```

单线程 asio::io_context 负责：

```text
Plot 注册/移除
resize 状态写入
timer async_wait
submitRender
prepareData
finishRender
jobState 推进
Color 发布
```

asio::thread_pool 负责：

```text
renderColor
draw
CPU 像素绘制
```

不允许再引入：

```text
TimerThread
按 Plot 名称绑定渲染线程
业务层 bindRenderThread
多个 QThread scheduler
```

所有 Plot 共用一个 scheduler。

## 11. 渲染管线和调度策略

渲染拆成两层：

```text
RenderPipeline：
    管 State/Input/Color 三缓冲、jobState、一次提交。

RenderSchedulePolicy：
    管什么时候触发一次提交。
```

核心边界：

```text
submitRenderOnce()
```

只负责：

```text
当前是否可以提交一帧。
如果可以，准备数据并投递 CPU render。
成功返回 true。
失败返回 false。
```

它不负责：

```text
限速
是否追帧
是否响应 timer
finish 后是否继续
```

这些全部属于调度策略。

Plot 级调度模式：

```text
Plot_Render_Schedule_Mode::Timer_Check
Plot_Render_Schedule_Mode::Chase_Latest
Plot_Render_Schedule_Mode::Max_Fps
Plot_Render_Schedule_Mode::Manual
```

语义：

```text
Timer_Check：
    只响应 asio timer。
    dirty 只推进版本，不立即提交。

Chase_Latest：
    dirty、timer、finish、manual 都会尝试提交。
    finish 后如果仍有新状态，会继续追最新帧。

Max_Fps：
    dirty 不丢弃。
    如果没到下一帧允许提交时间，只记录 pending 并安排 mSubmitTimer。
    到点后只提交最新状态。

Manual：
    只响应 Render_Request_Source::Manual。
```

请求来源：

```text
Render_Request_Source::Timer
Render_Request_Source::Dirty
Render_Request_Source::Finish
Render_Request_Source::Manual
```

`startRender(int fps)` 的语义改成：

```text
启动渲染调度。
设置 timer 检查频率。
是否按这个 fps 限制实际 render，由 Plot_Render_Schedule_Mode 决定。
```

严格限帧使用：

```text
Plot_Render_Schedule_Mode::Max_Fps
Plot::setMaxRenderFps()
```

asio timer 分成两个：

```text
mRenderTimer：
    周期性产生 Render_Request_Source::Timer。

mSubmitTimer：
    Max_Fps 或 RenderAble 局部 Max_Fps 的延迟提交唤醒。
```

RenderAble 级策略：

```text
RenderAble_Render_Schedule_Mode::Inherit
RenderAble_Render_Schedule_Mode::Chase_Latest
RenderAble_Render_Schedule_Mode::Max_Fps
RenderAble_Render_Schedule_Mode::On_Dirty
RenderAble_Render_Schedule_Mode::Manual
```

RenderAble 默认：

```text
Inherit
```

RenderAble 局部策略控制：

```text
本轮是否把该 RenderAble 的 State/Input 推进到 render 侧。
```

如果一个 RenderAble 没通过自己的策略闸门：

```text
prepareData 不执行。
render_state / RenderCache 保持上一帧。
draw 仍读取上一帧已经发布的 render 侧数据。
```

这样一个高频对象不会强迫静态对象每帧同步 State/Input。

独立像素缓存对象也走同一套 RenderAble 策略。

PerformanceShower 可以观测：

```text
Pipeline.renderScheduleMode
Pipeline.renderCheckFps
Pipeline.maxRenderFps
Pipeline.renderSubmitPending
RenderAble.<name>.renderScheduleMode
RenderAble.<name>.renderScheduleMaxFps
```

右键菜单的 Plot 基础栏提供：

```text
渲染策略下拉框
渲染检查FPS
最大渲染FPS
```

## 12. prepareData 边界

prepareData 的标准流程：

```text
syncStatePipeline()
读取 render_state 配置
读取 render_input 增量
更新 RenderCache
clearRenderInput()
```

syncStatePipeline 内部同时处理：

```text
State edit -> ready -> render
Input edit -> ready -> render
```

prepareData 不再持有 RenderData 对象级锁。

edit_state 写入必须通过 State_Edit lease。

edit_input 写入必须通过 Input_Edit lease。

RenderCache 只允许 render 流程内部更新。

prepareData 不应该写 Color。

prepareData 不应该触发 QWidget 绘制。

prepareData 不应该把 RenderCache 复制回 State。

## 13. draw 边界

draw 的标准流程：

```text
读取 render_state
读取 RenderCache
写入当前 render_color 对应的 QPainter
```

draw 不消费 Input。

draw 不修改 edit_state。

draw 不修改 ready_state。

draw 不做输入队列交换。

## 14. 数据分类规则

判断一个字段应该放哪里：

```text
需要被属性 getter/setter 表达，体积小，可复制：
    放 State。

外部不断输入，prepareData 批量消费：
    放 InputData。

由 render_state + render_input 计算出来，长期保留给 draw 使用：
    放 RenderData 私有缓存。

最终像素图：
    放 ColorBuffer。
```

错误分类：

```text
把 QVector 大缓存放 State。
把 QImage 放 State。
把输入 list 放 State。
把 RenderCache 放 InputData。
把配置项放 InputData。
```

## 15. 锁使用边界

radio 渲染链路不允许使用 RenderData 对象级大锁。

允许存在的同步只有下面几类：

```text
Triple_Role_Buffer_Control:
    使用 atomic busy / slot_state / wait / notify。
    只保护物理 buffer lease 和 role 交换。

RenderPipeline:
    使用 color buffer version / paintRequestPending / editStateVersion。
    只保护帧发布和 Qt update 请求合并。

Plot / RenderScheduler:
    mRenderEnabled / mDestroying / mActiveRenderTasks / pending render size / paint buffer acquire mode 使用 atomic。
    mRenderTaskMutex + mRenderTaskDone 只用于 Plot 析构等待已投递任务结束。
    RenderScheduler::postAndWait 的局部 mutex + condition_variable 只用于跨线程同步 shutdown/removePlot。

PerformanceShower:
    本身是 RenderAble。
    统计写入走 Input 三缓冲。
    统计 map 和显示列表是 Render 私有缓存。
    计数使用 CPP_Core/Core/Statistics::Speed_Statistics。
    耗时使用 CPP_Core/Core/Statistics::Value_Statistics。
    Value_Statistics 提供 instant / smooth / variation。
    开启独立像素缓存，不进入 Plot 整图 renderColor。
    统计写入按观察者节流刷新，不允许每条事件触发整图 dirty。
```

不允许重新引入：

```text
RenderData::mBufferLock
prepareData 全局锁
render size mutex
Graphic resize mutex
HoverInfo 独立 mutex
PrePareMutiDataMutex
SpinLock / SpinLockGuard
Singleton mutex + atomic 双重检查
```

prepareData 和 draw 的并发边界由调度流保证：

```text
submitRender 进入 asio scheduler。
scheduler 串行推进 jobState。
prepareData 在 scheduler 阶段消费 State/Input。
renderColor 在 asio thread_pool 阶段只读取 State_Render 和 RenderCache。
finishRender 回到 scheduler 发布 Color。
```

CPP_Core 和第三方库内部同步不属于 radio 渲染架构边界：

```text
RingBuffer_MT / StreamRingBuffer_MT:
    通用多线程容器版本。

SM_RingBuffer / Cross_Process_Mutex:
    跨进程共享内存同步。

Frequency_Limit:
    通用频率限制器。

spdlog / asio / googlepinyin:
    第三方或通用库内部同步。
```

这些同步不能向 RenderData、prepareData、draw 路径扩散。

## 16. 性能统计

性能测试框是 RenderAble，不再是 QWidget。

开启和停止性能测试只修改 PerformanceShower 的 enabled 状态。

停止性能测试不删除 PerformanceShower 对象。

原因：

```text
render/scheduler/cpu 线程可能持有 Cacl。
Cacl 需要在析构时写统计。
如果停止性能测试时删除 QWidget，会形成跨线程悬挂指针。
```

统计写入走 Input 三缓冲：

统计类型：

```text
Duration：
    阶段耗时，使用 Psc::Value_Statistics。

Counter：
    事件频率，使用 Psc::Speed_Statistics。

Gauge：
    当前数值状态。

State：
    当前文本状态。
```

统计链路：

```text
PerformanceShower::recordDuration / incrementCounter / setGauge / setState
    ↓
PerformanceShowerInputData events
    ↓
prepareData()
    ↓
PerformanceShowerPrivate::mScopes
    ↓
draw()
```

Value_Statistics 的含义：

```text
instant：
    本次采样值。

average：
    保留旧字段语义的 EWMA 平均值。

smooth：
    类似 SRTT 的平滑值。

variation：
    类似 RTTVAR 的波动估计。
    它不是严格方差或标准差，而是 instant 与 smooth 偏差的 EWMA。

p95：
    最近 64 次采样的 95 分位估计。
```

性能框当前分组：

```text
Pipeline：
    schedulerQueueWait
    requestToSubmit
    cpuQueueWait
    prepareData
    renderColor
    draw
    publishRenderColor
    updateToPaint
    paintEvent
    jobState
    activeRenderTasks
    renderEnabled
    paintBufferAcquireMode
    renderAbleDefaultAcquireMode
    paintRequestPending
    editStateVersion
    renderStateVersion

RenderAble：
    <RenderAble>.prepareData
    <RenderAble>.draw
    <RenderAble>.stateLeaseMiss
    <RenderAble>.bufferAcquireMode

Buffer：
    Color.mark/swap/busy/swapNoNeed/wait
    <RenderAble>.State.mark/swap/busy/swapNoNeed/wait
    <RenderAble>.Input.mark/swap/busy/swapNoNeed/wait
```

性能框显示策略：

```text
默认视图：
    Plot

Plot 视图显示：
    Pipeline
    General
    Color buffer

左侧栏显示：
    一级：Plot / 每个有统计数据的 RenderAble
    二级：当前对象拥有的数据面

Plot 的二级数据面：
    Pipeline
    General
    Color

RenderAble 的二级数据面：
    RenderAble
    State
    Input

内容区：
    不显示 [Pipeline] / [Buffer] / [RenderAble] 这种分组标题。
    当前层级已经表达数据归属。
    指标名会去掉已由层级表达的对象和缓冲前缀。
内容过长时显示滚动条，鼠标滚轮滚动内容区。

布局宽度策略：
    左侧两级栏宽只在栏项目变化时重算。
    右侧指标内容宽只在切换视图时重算。
```

PerformanceShower 的刷新策略：

```text
统计事件：
    写入 PerformanceShower Input。
    不逐条 markRenderDirty。

观察者刷新：
    普通统计事件最多 10Hz 触发一次独立像素缓存刷新。
    Clear / 选择视图 / 滚动 立即刷新。

排除项：
    PerformanceShower 自身不进入 RenderAble 性能统计列表。
```

Pull snapshot 的含义：

```text
Push gauge：
    每帧主动把每个 RenderAble 的每个 Buffer 统计写成事件。
    会制造大量 PerformanceShower Input。

Pull snapshot：
    Buffer 控制器只维护 atomic 计数。
    性能面板刷新时按需读取当前快照。
    没有面板刷新就不产生统计事件。
```

完整性能面板最终应该使用 QWidget：

```text
Overlay：
    只显示摘要。

右键菜单 QWidget 面板：
    显示完整 Pipeline / RenderAble / Buffer 树。
    使用低频 pull snapshot 刷新。
```

## 17. 最终规则

```text
State 负责配置快照。
Input 负责增量输入交接。
RenderCache 负责渲染侧长期缓存。
Color 负责像素交接。
```

一句话：

```text
每个 RenderAble 使用 State 三缓冲发布小状态，使用 Input 三缓冲发布增量输入，使用 RenderData 私有缓存保存重型渲染中间结果，使用 Color 三缓冲把后台像素图交给 Qt 主线程。
```
