9.4 KiB
图表渲染整体架构设计
1. 总体模型
每个 RenderAble 拆成四类数据:
State 三缓冲
Input 三缓冲
Render 私有缓存
Color 三缓冲
职责边界:
State:
小型、可复制、表示显示配置和稳定状态。
Input:
外部输入到渲染线程的增量数据。
RenderCache:
渲染侧长期缓存,不复制,不参与状态交换。
Color:
后台生成的像素图,交给 Qt 主线程贴图。
核心原则:
配置走 State。
样本走 Input。
缓存留在 RenderData 私有成员。
像素走 Color。
不能把流式样本、环形缓冲、累计矩阵塞进 State。
State 的同步语义是复制。
Input 的同步语义是角色交换。
RenderCache 的语义是渲染侧所有权。
2. 整体流程
用户操作 / 属性修改
↓
修改 edit_state
↓
state edit -> ready 复制
↓
state ready -> render 交换
数据输入 / giveData
↓
追加到 edit_input
↓
input edit -> ready 交换
↓
input ready -> render 交换
↓
prepareData 消费 render_input
↓
更新 RenderCache
↓
clear render_input
draw
↓
读取 render_state
↓
读取 RenderCache
↓
写 render_color
↓
color render -> ready 交换
↓
Qt paintEvent 消费 ready_color 到 front_color
完整数据链路:
edit_state + edit_input
↓
prepareData
render_state + render_input
↓
RenderCache
↓
render_color
↓
ready_color
↓
front_color
3. 三缓冲控制器
State、Input、Color 的角色交换都使用同一个控制器:
Triple_Role_Buffer_Control
控制器只负责:
role -> physical index
busy 标记
lease
swap_role
它不理解实际数据类型。
实际数据可以是:
RenderState
InputData
ColorBuffer
控制器语义:
slot_state 管角色映射。
busy 管物理占用。
lease 管一次具体使用。
swap_role 只交换角色,不复制业务数据。
长时间使用某个角色时,必须释放 lease,不允许按 role 释放。
因为使用期间 role 可能已经被交换。
4. State 三缓冲
State 有三个角色:
State_Edit
State_Ready
State_Render
State 里只放:
坐标轴指针
范围
颜色
画笔
字体
hover 状态
marker 状态
模式开关
点数配置
State 里不放:
输入样本列表
环形缓冲区
QImage
累计矩阵
曲线当前数据缓存
WaterFall 行缓存
Afterglow 能量累计缓存
State 同步流程:
如果 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 的核心意义:
把可复制配置从编辑侧稳定发布给渲染侧。
跳过中间状态。
保证 CPU 绘制时读取稳定 render_state。
5. Input 三缓冲
Input 有三个角色:
Input_Edit
Input_Ready
Input_Render
InputData 里放增量输入:
WaterFall 新行
AudioFrequent 新功率点
TimeAxis 新时间点
SweepFrequent 新块数据
Planisphere 新星座点
Spectrum 新曲线样本
Afterglow 新功率行
输入线程只追加到 edit_input。
追加成功后:
input_slot.version = ++mInputVersion
input_slot.pending = true
requestRender()
Input 同步流程:
如果 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 消费流程:
auto input = renderInputData()
for each delta in input:
更新 RenderCache
clearRenderInput()
clearRenderInput 必须同时完成:
清空 render_input 数据
render_input.pending = false
Input 三缓冲的核心意义:
输入数据不复制到 State。
输入数据不触发 RenderState 大对象复制。
渲染侧按批消费增量。
edit_input 可以在下一轮继续接收新数据。
6. Render 私有缓存
RenderCache 是 RenderData 的私有成员,不参与三缓冲复制。
典型对象:
QImage image
WaterFallRingBuffer ringBuffer
TimeAxis::mRingBuffer
SweepFrequent::mFrequents
Planisphere::dataList
Afterglow cachedPowerData / oldCachePowerData / mergedPowerData
Spectrum curPowers / maxPowers / minPowers / frequents
RenderCache 的来源:
render_state 配置
render_input 增量
RenderCache 的更新位置:
prepareData()
RenderCache 的读取位置:
draw()
RenderCache 不应该由属性 setter 直接修改。
属性 setter 只改 edit_state。
数据输入函数只追加 edit_input。
7. Color 三缓冲
Color 有三个角色:
Color_Front
Color_Ready
Color_Render
Color_Render:
CPU 后台线程写入。
Color_Ready:
后台完成后等待 Qt 主线程消费。
Color_Front:
Qt paintEvent 当前显示。
发布流程:
render_color.version 新于 ready_color.version:
swap ready_color, render_color
ready_color.version 新于 front_color.version:
request update
paintEvent 流程:
try swap front_color, ready_color
mark front_color
drawImage(front_color)
unmark front_color
paintEvent 只能使用 Try,不允许等待后台。
8. 调度版本
渲染调度使用 RenderPipeline 的帧版本。
State dirty 和 Input dirty 都会推进帧版本。
区别:
State dirty:
edit_state.version++
frame_version++
Input dirty:
input_version++
frame_version++
Input dirty 不递增 edit_state.version。
这点非常重要。
它保证流式输入不会导致 RenderState 全量复制。
9. prepareData 边界
prepareData 的标准流程:
syncStatePipeline()
读取 render_state 配置
读取 render_input 增量
更新 RenderCache
clearRenderInput()
syncStatePipeline 内部同时处理:
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。
10. draw 边界
draw 的标准流程:
读取 render_state
读取 RenderCache
写入当前 render_color 对应的 QPainter
draw 不消费 Input。
draw 不修改 edit_state。
draw 不修改 ready_state。
draw 不做输入队列交换。
11. 数据分类规则
判断一个字段应该放哪里:
需要被属性 getter/setter 表达,体积小,可复制:
放 State。
外部不断输入,prepareData 批量消费:
放 InputData。
由 render_state + render_input 计算出来,长期保留给 draw 使用:
放 RenderData 私有缓存。
最终像素图:
放 ColorBuffer。
错误分类:
把 QVector 大缓存放 State。
把 QImage 放 State。
把输入 list 放 State。
把 RenderCache 放 InputData。
把配置项放 InputData。
12. 锁使用边界
radio 渲染链路不允许使用 RenderData 对象级大锁。
允许存在的同步只有下面几类:
Triple_Role_Buffer_Control:
使用 atomic busy / slot_state / wait / notify。
只保护物理 buffer lease 和 role 交换。
RenderPipeline:
使用 color buffer version / paintRequestPending / editStateVersion。
只保护帧发布和 Qt update 请求合并。
Plot / TimerThread:
mRenderEnabled / mDestroying / mActiveRenderTasks / pending render size 使用 atomic。
mRenderTaskMutex + mRenderTaskDone 只用于 Plot 析构等待已投递任务结束。
TimerThread::postAndWait 的局部 mutex + condition_variable 只用于跨线程同步 shutdown/removePlot。
PerformanceShower:
mMutex 只保护统计 map 和显示列表。
不允许重新引入:
RenderData::mBufferLock
prepareData 全局锁
render size mutex
Graphic resize mutex
HoverInfo 独立 mutex
PrePareMutiDataMutex
SpinLock / SpinLockGuard
Singleton mutex + atomic 双重检查
prepareData 和 draw 的并发边界由调度流保证:
submitRender 进入 asio scheduler。
scheduler 串行推进 jobState。
prepareData 在 scheduler 阶段消费 State/Input。
renderColor 在 CPU 阶段只读取 State_Render 和 RenderCache。
finishRender 回到 scheduler 发布 Color。
CPP_Core 和第三方库内部同步不属于 radio 渲染架构边界:
RingBuffer_MT / StreamRingBuffer_MT:
通用多线程容器版本。
SM_RingBuffer / Cross_Process_Mutex:
跨进程共享内存同步。
Frequency_Limit:
通用频率限制器。
spdlog / asio / googlepinyin:
第三方或通用库内部同步。
这些同步不能向 RenderData、prepareData、draw 路径扩散。
13. 最终规则
State 负责配置快照。
Input 负责增量输入交接。
RenderCache 负责渲染侧长期缓存。
Color 负责像素交接。
一句话:
每个 RenderAble 使用 State 三缓冲发布小状态,使用 Input 三缓冲发布增量输入,使用 RenderData 私有缓存保存重型渲染中间结果,使用 Color 三缓冲把后台像素图交给 Qt 主线程。