PySide6 工业上位机的实时帧链、零拷贝与跨语言架构
适合读者:正在用 PySide6 写工业控制、相机或数据采集上位机;想理解"Qt 主线程真的只能有一个,但 GIL 不是性能末日";被 4 MiB/帧的大块数据复制、OpenGL 上传黑屏、PyInstaller 产物臃肿反复折磨;在做 Python/C++ 混合架构,想搞清楚零拷贝究竟在哪里剪。
主线有三条:线程与背压、零拷贝与跨语言边界、打包与解释器。每一节按「原理→证据→方案→代价」组织。
一、PySide6 的"主线程只有一个"不等于"只能有一个真线程"
PySide6 的事件循环确实只在 GUI 主线程,但 Python 的 QThread、threading.Thread、C++ 的 std::thread 都是真实 OS 线程。GIL 只限制同一解释器 中纯 Python 字节码 的并行执行;NumPy、ctypes.CDLL 调用(释放 GIL)、C++ worker、OpenGL 驱动、USB SDK 都能在 native 区域并行。
所以正确目标不是"创建更多 QThread",而是缩短 Python 持有 GIL 的热路径,把大计算和大数据移动留在 native 层。
#mermaid-svg-r9aV2l5XIQUARbOt{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-r9aV2l5XIQUARbOt .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-r9aV2l5XIQUARbOt .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-r9aV2l5XIQUARbOt .error-icon{fill:#552222;}#mermaid-svg-r9aV2l5XIQUARbOt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-r9aV2l5XIQUARbOt .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-r9aV2l5XIQUARbOt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-r9aV2l5XIQUARbOt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-r9aV2l5XIQUARbOt .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-r9aV2l5XIQUARbOt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-r9aV2l5XIQUARbOt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-r9aV2l5XIQUARbOt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-r9aV2l5XIQUARbOt .marker.cross{stroke:#333333;}#mermaid-svg-r9aV2l5XIQUARbOt svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-r9aV2l5XIQUARbOt p{margin:0;}#mermaid-svg-r9aV2l5XIQUARbOt .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-r9aV2l5XIQUARbOt .cluster-label text{fill:#333;}#mermaid-svg-r9aV2l5XIQUARbOt .cluster-label span{color:#333;}#mermaid-svg-r9aV2l5XIQUARbOt .cluster-label span p{background-color:transparent;}#mermaid-svg-r9aV2l5XIQUARbOt .label text,#mermaid-svg-r9aV2l5XIQUARbOt span{fill:#333;color:#333;}#mermaid-svg-r9aV2l5XIQUARbOt .node rect,#mermaid-svg-r9aV2l5XIQUARbOt .node circle,#mermaid-svg-r9aV2l5XIQUARbOt .node ellipse,#mermaid-svg-r9aV2l5XIQUARbOt .node polygon,#mermaid-svg-r9aV2l5XIQUARbOt .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-r9aV2l5XIQUARbOt .rough-node .label text,#mermaid-svg-r9aV2l5XIQUARbOt .node .label text,#mermaid-svg-r9aV2l5XIQUARbOt .image-shape .label,#mermaid-svg-r9aV2l5XIQUARbOt .icon-shape .label{text-anchor:middle;}#mermaid-svg-r9aV2l5XIQUARbOt .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-r9aV2l5XIQUARbOt .rough-node .label,#mermaid-svg-r9aV2l5XIQUARbOt .node .label,#mermaid-svg-r9aV2l5XIQUARbOt .image-shape .label,#mermaid-svg-r9aV2l5XIQUARbOt .icon-shape .label{text-align:center;}#mermaid-svg-r9aV2l5XIQUARbOt .node.clickable{cursor:pointer;}#mermaid-svg-r9aV2l5XIQUARbOt .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-r9aV2l5XIQUARbOt .arrowheadPath{fill:#333333;}#mermaid-svg-r9aV2l5XIQUARbOt .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-r9aV2l5XIQUARbOt .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-r9aV2l5XIQUARbOt .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-r9aV2l5XIQUARbOt .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-r9aV2l5XIQUARbOt .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-r9aV2l5XIQUARbOt .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-r9aV2l5XIQUARbOt .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-r9aV2l5XIQUARbOt .cluster text{fill:#333;}#mermaid-svg-r9aV2l5XIQUARbOt .cluster span{color:#333;}#mermaid-svg-r9aV2l5XIQUARbOt div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-r9aV2l5XIQUARbOt .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-r9aV2l5XIQUARbOt rect.text{fill:none;stroke-width:0;}#mermaid-svg-r9aV2l5XIQUARbOt .icon-shape,#mermaid-svg-r9aV2l5XIQUARbOt .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-r9aV2l5XIQUARbOt .icon-shape p,#mermaid-svg-r9aV2l5XIQUARbOt .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-r9aV2l5XIQUARbOt .icon-shape .label rect,#mermaid-svg-r9aV2l5XIQUARbOt .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-r9aV2l5XIQUARbOt .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-r9aV2l5XIQUARbOt .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-r9aV2l5XIQUARbOt :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-r9aV2l5XIQUARbOt .gui>*{fill:#1f3a5f!important;stroke:#7fb3ff!important;color:#ffffff!important;}#mermaid-svg-r9aV2l5XIQUARbOt .gui span{fill:#1f3a5f!important;stroke:#7fb3ff!important;color:#ffffff!important;}#mermaid-svg-r9aV2l5XIQUARbOt .gui tspan{fill:#ffffff!important;}#mermaid-svg-r9aV2l5XIQUARbOt .worker>*{fill:#3a5a3a!important;stroke:#9ccc65!important;color:#ffffff!important;}#mermaid-svg-r9aV2l5XIQUARbOt .worker span{fill:#3a5a3a!important;stroke:#9ccc65!important;color:#ffffff!important;}#mermaid-svg-r9aV2l5XIQUARbOt .worker tspan{fill:#ffffff!important;}#mermaid-svg-r9aV2l5XIQUARbOt .native>*{fill:#5a3a3a!important;stroke:#ff8a80!important;color:#ffffff!important;}#mermaid-svg-r9aV2l5XIQUARbOt .native span{fill:#5a3a3a!important;stroke:#ff8a80!important;color:#ffffff!important;}#mermaid-svg-r9aV2l5XIQUARbOt .native tspan{fill:#ffffff!important;}#mermaid-svg-r9aV2l5XIQUARbOt .device>*{fill:#5a4a1f!important;stroke:#ffd54f!important;color:#000000!important;}#mermaid-svg-r9aV2l5XIQUARbOt .device span{fill:#5a4a1f!important;stroke:#ffd54f!important;color:#000000!important;}#mermaid-svg-r9aV2l5XIQUARbOt .device tspan{fill:#000000!important;} queued signal
small payload
dirty flag
latest token
ctypes 释放 GIL
FrameChannel
SPSC ring
token + metadata
Qt GUI 主线程
事件/控件/OpenGL
CommandWorker
唯一设备 owner
FramePumpWorker
采集轮询
hcs_core.dll
C++ 真线程
USB / UVC
SDK
GUI 主线程允许:事件、控件、Qt 状态、OpenGL context、轻量 token 拉取。
GUI 主线程禁止:USB 往返、join/wait、整帧 NumPy reduction、编码、写盘。
二、线程拓扑:控制面与数据面分离,三类 QoS 队列
把实时数据、可丢数据、可靠数据、控制消息分别给不同 QoS:
#mermaid-svg-MaS6y3mL05Cd8mWb{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-MaS6y3mL05Cd8mWb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-MaS6y3mL05Cd8mWb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-MaS6y3mL05Cd8mWb .error-icon{fill:#552222;}#mermaid-svg-MaS6y3mL05Cd8mWb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MaS6y3mL05Cd8mWb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-MaS6y3mL05Cd8mWb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MaS6y3mL05Cd8mWb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MaS6y3mL05Cd8mWb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-MaS6y3mL05Cd8mWb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MaS6y3mL05Cd8mWb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MaS6y3mL05Cd8mWb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MaS6y3mL05Cd8mWb .marker.cross{stroke:#333333;}#mermaid-svg-MaS6y3mL05Cd8mWb svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MaS6y3mL05Cd8mWb p{margin:0;}#mermaid-svg-MaS6y3mL05Cd8mWb .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-MaS6y3mL05Cd8mWb .cluster-label text{fill:#333;}#mermaid-svg-MaS6y3mL05Cd8mWb .cluster-label span{color:#333;}#mermaid-svg-MaS6y3mL05Cd8mWb .cluster-label span p{background-color:transparent;}#mermaid-svg-MaS6y3mL05Cd8mWb .label text,#mermaid-svg-MaS6y3mL05Cd8mWb span{fill:#333;color:#333;}#mermaid-svg-MaS6y3mL05Cd8mWb .node rect,#mermaid-svg-MaS6y3mL05Cd8mWb .node circle,#mermaid-svg-MaS6y3mL05Cd8mWb .node ellipse,#mermaid-svg-MaS6y3mL05Cd8mWb .node polygon,#mermaid-svg-MaS6y3mL05Cd8mWb .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-MaS6y3mL05Cd8mWb .rough-node .label text,#mermaid-svg-MaS6y3mL05Cd8mWb .node .label text,#mermaid-svg-MaS6y3mL05Cd8mWb .image-shape .label,#mermaid-svg-MaS6y3mL05Cd8mWb .icon-shape .label{text-anchor:middle;}#mermaid-svg-MaS6y3mL05Cd8mWb .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-MaS6y3mL05Cd8mWb .rough-node .label,#mermaid-svg-MaS6y3mL05Cd8mWb .node .label,#mermaid-svg-MaS6y3mL05Cd8mWb .image-shape .label,#mermaid-svg-MaS6y3mL05Cd8mWb .icon-shape .label{text-align:center;}#mermaid-svg-MaS6y3mL05Cd8mWb .node.clickable{cursor:pointer;}#mermaid-svg-MaS6y3mL05Cd8mWb .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-MaS6y3mL05Cd8mWb .arrowheadPath{fill:#333333;}#mermaid-svg-MaS6y3mL05Cd8mWb .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-MaS6y3mL05Cd8mWb .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-MaS6y3mL05Cd8mWb .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MaS6y3mL05Cd8mWb .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-MaS6y3mL05Cd8mWb .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MaS6y3mL05Cd8mWb .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-MaS6y3mL05Cd8mWb .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-MaS6y3mL05Cd8mWb .cluster text{fill:#333;}#mermaid-svg-MaS6y3mL05Cd8mWb .cluster span{color:#333;}#mermaid-svg-MaS6y3mL05Cd8mWb div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-MaS6y3mL05Cd8mWb .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-MaS6y3mL05Cd8mWb rect.text{fill:none;stroke-width:0;}#mermaid-svg-MaS6y3mL05Cd8mWb .icon-shape,#mermaid-svg-MaS6y3mL05Cd8mWb .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MaS6y3mL05Cd8mWb .icon-shape p,#mermaid-svg-MaS6y3mL05Cd8mWb .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-MaS6y3mL05Cd8mWb .icon-shape .label rect,#mermaid-svg-MaS6y3mL05Cd8mWb .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MaS6y3mL05Cd8mWb .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-MaS6y3mL05Cd8mWb .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-MaS6y3mL05Cd8mWb :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-MaS6y3mL05Cd8mWb .gui>*{fill:#1f3a5f!important;stroke:#7fb3ff!important;color:#ffffff!important;}#mermaid-svg-MaS6y3mL05Cd8mWb .gui span{fill:#1f3a5f!important;stroke:#7fb3ff!important;color:#ffffff!important;}#mermaid-svg-MaS6y3mL05Cd8mWb .gui tspan{fill:#ffffff!important;}#mermaid-svg-MaS6y3mL05Cd8mWb .worker>*{fill:#3a5a3a!important;stroke:#9ccc65!important;color:#ffffff!important;}#mermaid-svg-MaS6y3mL05Cd8mWb .worker span{fill:#3a5a3a!important;stroke:#9ccc65!important;color:#ffffff!important;}#mermaid-svg-MaS6y3mL05Cd8mWb .worker tspan{fill:#ffffff!important;}#mermaid-svg-MaS6y3mL05Cd8mWb .native>*{fill:#5a3a3a!important;stroke:#ff8a80!important;color:#ffffff!important;}#mermaid-svg-MaS6y3mL05Cd8mWb .native span{fill:#5a3a3a!important;stroke:#ff8a80!important;color:#ffffff!important;}#mermaid-svg-MaS6y3mL05Cd8mWb .native tspan{fill:#ffffff!important;}#mermaid-svg-MaS6y3mL05Cd8mWb .device>*{fill:#5a4a1f!important;stroke:#ffd54f!important;color:#000000!important;}#mermaid-svg-MaS6y3mL05Cd8mWb .device span{fill:#5a4a1f!important;stroke:#ffd54f!important;color:#000000!important;}#mermaid-svg-MaS6y3mL05Cd8mWb .device tspan{fill:#000000!important;} 数据面(大帧/低延迟)
控制面(小消息/强顺序)
enqueue HIGH/NORMAL/LOW
ctypes 释放 GIL
latest-wins
coalescing
reliable ring
dirty flag
latest token
GUI Qt 主线程
CommandWorker
串行 + 优先级 + coalesce
hcs_core Control/AsyncBus
设备命令通道
USB 30 fps
C++ CaptureThread
高优先级
C++ Parse/Normalize
Y16 + 信息行
FrameChannel
token + generation
DisplayPump
AnalyticsPool
温度/直方图
RecorderQueue
#mermaid-svg-KVcUTDABrrGk8XBn{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-KVcUTDABrrGk8XBn .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KVcUTDABrrGk8XBn .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KVcUTDABrrGk8XBn .error-icon{fill:#552222;}#mermaid-svg-KVcUTDABrrGk8XBn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KVcUTDABrrGk8XBn .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KVcUTDABrrGk8XBn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KVcUTDABrrGk8XBn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KVcUTDABrrGk8XBn .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KVcUTDABrrGk8XBn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KVcUTDABrrGk8XBn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KVcUTDABrrGk8XBn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KVcUTDABrrGk8XBn .marker.cross{stroke:#333333;}#mermaid-svg-KVcUTDABrrGk8XBn svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KVcUTDABrrGk8XBn p{margin:0;}#mermaid-svg-KVcUTDABrrGk8XBn .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-KVcUTDABrrGk8XBn .cluster-label text{fill:#333;}#mermaid-svg-KVcUTDABrrGk8XBn .cluster-label span{color:#333;}#mermaid-svg-KVcUTDABrrGk8XBn .cluster-label span p{background-color:transparent;}#mermaid-svg-KVcUTDABrrGk8XBn .label text,#mermaid-svg-KVcUTDABrrGk8XBn span{fill:#333;color:#333;}#mermaid-svg-KVcUTDABrrGk8XBn .node rect,#mermaid-svg-KVcUTDABrrGk8XBn .node circle,#mermaid-svg-KVcUTDABrrGk8XBn .node ellipse,#mermaid-svg-KVcUTDABrrGk8XBn .node polygon,#mermaid-svg-KVcUTDABrrGk8XBn .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KVcUTDABrrGk8XBn .rough-node .label text,#mermaid-svg-KVcUTDABrrGk8XBn .node .label text,#mermaid-svg-KVcUTDABrrGk8XBn .image-shape .label,#mermaid-svg-KVcUTDABrrGk8XBn .icon-shape .label{text-anchor:middle;}#mermaid-svg-KVcUTDABrrGk8XBn .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-KVcUTDABrrGk8XBn .rough-node .label,#mermaid-svg-KVcUTDABrrGk8XBn .node .label,#mermaid-svg-KVcUTDABrrGk8XBn .image-shape .label,#mermaid-svg-KVcUTDABrrGk8XBn .icon-shape .label{text-align:center;}#mermaid-svg-KVcUTDABrrGk8XBn .node.clickable{cursor:pointer;}#mermaid-svg-KVcUTDABrrGk8XBn .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-KVcUTDABrrGk8XBn .arrowheadPath{fill:#333333;}#mermaid-svg-KVcUTDABrrGk8XBn .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-KVcUTDABrrGk8XBn .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-KVcUTDABrrGk8XBn .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KVcUTDABrrGk8XBn .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-KVcUTDABrrGk8XBn .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KVcUTDABrrGk8XBn .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-KVcUTDABrrGk8XBn .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-KVcUTDABrrGk8XBn .cluster text{fill:#333;}#mermaid-svg-KVcUTDABrrGk8XBn .cluster span{color:#333;}#mermaid-svg-KVcUTDABrrGk8XBn div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-KVcUTDABrrGk8XBn .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-KVcUTDABrrGk8XBn rect.text{fill:none;stroke-width:0;}#mermaid-svg-KVcUTDABrrGk8XBn .icon-shape,#mermaid-svg-KVcUTDABrrGk8XBn .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KVcUTDABrrGk8XBn .icon-shape p,#mermaid-svg-KVcUTDABrrGk8XBn .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-KVcUTDABrrGk8XBn .icon-shape .label rect,#mermaid-svg-KVcUTDABrrGk8XBn .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KVcUTDABrrGk8XBn .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-KVcUTDABrrGk8XBn .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-KVcUTDABrrGk8XBn :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-KVcUTDABrrGk8XBn .reliable>*{fill:#1f4f7a!important;stroke:#4fc3f7!important;color:#ffffff!important;}#mermaid-svg-KVcUTDABrrGk8XBn .reliable span{fill:#1f4f7a!important;stroke:#4fc3f7!important;color:#ffffff!important;}#mermaid-svg-KVcUTDABrrGk8XBn .reliable tspan{fill:#ffffff!important;}#mermaid-svg-KVcUTDABrrGk8XBn .latest>*{fill:#7a4f1f!important;stroke:#ffb74d!important;color:#ffffff!important;}#mermaid-svg-KVcUTDABrrGk8XBn .latest span{fill:#7a4f1f!important;stroke:#ffb74d!important;color:#ffffff!important;}#mermaid-svg-KVcUTDABrrGk8XBn .latest tspan{fill:#ffffff!important;}#mermaid-svg-KVcUTDABrrGk8XBn .coalesce>*{fill:#4f7a1f!important;stroke:#aed581!important;color:#ffffff!important;}#mermaid-svg-KVcUTDABrrGk8XBn .coalesce span{fill:#4f7a1f!important;stroke:#aed581!important;color:#ffffff!important;}#mermaid-svg-KVcUTDABrrGk8XBn .coalesce tspan{fill:#ffffff!important;}#mermaid-svg-KVcUTDABrrGk8XBn .priority>*{fill:#7a1f4f!important;stroke:#f48fb1!important;color:#ffffff!important;}#mermaid-svg-KVcUTDABrrGk8XBn .priority span{fill:#7a1f4f!important;stroke:#f48fb1!important;color:#ffffff!important;}#mermaid-svg-KVcUTDABrrGk8XBn .priority tspan{fill:#ffffff!important;}#mermaid-svg-KVcUTDABrrGk8XBn .fault>*{fill:#7a1f1f!important;stroke:#ef5350!important;color:#ffffff!important;}#mermaid-svg-KVcUTDABrrGk8XBn .fault span{fill:#7a1f1f!important;stroke:#ef5350!important;color:#ffffff!important;}#mermaid-svg-KVcUTDABrrGk8XBn .fault tspan{fill:#ffffff!important;}#mermaid-svg-KVcUTDABrrGk8XBn .warn>*{fill:#7a5a1f!important;stroke:#ffd54f!important;color:#000000!important;}#mermaid-svg-KVcUTDABrrGk8XBn .warn span{fill:#7a5a1f!important;stroke:#ffd54f!important;color:#000000!important;}#mermaid-svg-KVcUTDABrrGk8XBn .warn tspan{fill:#000000!important;}#mermaid-svg-KVcUTDABrrGk8XBn .ok>*{fill:#1f4f1f!important;stroke:#66bb6a!important;color:#ffffff!important;}#mermaid-svg-KVcUTDABrrGk8XBn .ok span{fill:#1f4f1f!important;stroke:#66bb6a!important;color:#ffffff!important;}#mermaid-svg-KVcUTDABrrGk8XBn .ok tspan{fill:#ffffff!important;} 记 overrun
不静默覆盖
覆盖旧 token
记 display_dropped
满载停止告警
结果带 frame_id
满载合并/拒绝
Capture SPSC
可靠有界
Fault
Display latest slot
最新帧胜出
继续
Recorder 可靠队列
显式停止
Analytics coalescing
按 task key 合并
继续
Command 优先级
HIGH/NORMAL/LOW
继续
要点:
- 采集线程(C++) 只做"拿一帧、盖章、发布"。不调 Python、不打日志(异步)、不进 Python GIL。
- Python FramePump :
QObject放到长驻QThread,每 tick 只发 latest descriptor + dirty flag。不发Signal(object)携带每帧 ndarray,否则 Qt posted-event 队列会积压。 - CommandWorker :唯一设备 owner,串行执行
get/set/exec,按 HIGH/NORMAL/LOW 优先级,滑块按(panel, key)coalesce;GUI 只 enqueue,绝不直接同步调真实设备。 - PySide6 的
QThread易踩点 :QThread对象本身仍属于创建它的线程(一般是 GUI),只有worker.moveToThread(thread)之后 worker 才在新线程里跑。
读者读到这里自然会有两个问题:上面这个 FrameChannel 上的"零拷贝"到底是什么,4 MiB 帧到底怎么走才不被反复复制?这是接下来 §三 的主题。
三、零拷贝的真实定义:哪里可以证明生命周期
"零拷贝"不是"哪里都不复制",而是大帧在可证明的 slot lease(代数、引用计数、显式 release)下只传递视图;写盘、编码、跨生命周期或无法证明所有权时允许一次稳定复制。
OpenGL 上传 Python consumer FramePool slot C++ CaptureThread USB SDK OpenGL 上传 Python consumer FramePool slot C++ CaptureThread USB SDK #mermaid-svg-wPdnNDTKK4LLbQom{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-wPdnNDTKK4LLbQom .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wPdnNDTKK4LLbQom .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wPdnNDTKK4LLbQom .error-icon{fill:#552222;}#mermaid-svg-wPdnNDTKK4LLbQom .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wPdnNDTKK4LLbQom .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wPdnNDTKK4LLbQom .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wPdnNDTKK4LLbQom .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wPdnNDTKK4LLbQom .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wPdnNDTKK4LLbQom .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wPdnNDTKK4LLbQom .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wPdnNDTKK4LLbQom .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wPdnNDTKK4LLbQom .marker.cross{stroke:#333333;}#mermaid-svg-wPdnNDTKK4LLbQom svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wPdnNDTKK4LLbQom p{margin:0;}#mermaid-svg-wPdnNDTKK4LLbQom .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-wPdnNDTKK4LLbQom text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-wPdnNDTKK4LLbQom .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-wPdnNDTKK4LLbQom .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-wPdnNDTKK4LLbQom .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-wPdnNDTKK4LLbQom .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-wPdnNDTKK4LLbQom #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-wPdnNDTKK4LLbQom .sequenceNumber{fill:white;}#mermaid-svg-wPdnNDTKK4LLbQom #sequencenumber{fill:#333;}#mermaid-svg-wPdnNDTKK4LLbQom #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-wPdnNDTKK4LLbQom .messageText{fill:#333;stroke:none;}#mermaid-svg-wPdnNDTKK4LLbQom .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-wPdnNDTKK4LLbQom .labelText,#mermaid-svg-wPdnNDTKK4LLbQom .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-wPdnNDTKK4LLbQom .loopText,#mermaid-svg-wPdnNDTKK4LLbQom .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-wPdnNDTKK4LLbQom .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-wPdnNDTKK4LLbQom .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-wPdnNDTKK4LLbQom .noteText,#mermaid-svg-wPdnNDTKK4LLbQom .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-wPdnNDTKK4LLbQom .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-wPdnNDTKK4LLbQom .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-wPdnNDTKK4LLbQom .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-wPdnNDTKK4LLbQom .actorPopupMenu{position:absolute;}#mermaid-svg-wPdnNDTKK4LLbQom .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-wPdnNDTKK4LLbQom .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-wPdnNDTKK4LLbQom .actor-man circle,#mermaid-svg-wPdnNDTKK4LLbQom line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-wPdnNDTKK4LLbQom :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 不调 Python、不打日志、不做 reduction stop 顺序:停 producer → drain consumer → close 借用指针 + frame_bytesacquire slotwrite payloadstate=READY (release)publish token (frame_id, generation)acquire(token)numpy view (无 copy)glTexSubImage2D(view)release(token)
#mermaid-svg-WqXeFLhGGHmHOPBZ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-WqXeFLhGGHmHOPBZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-WqXeFLhGGHmHOPBZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-WqXeFLhGGHmHOPBZ .error-icon{fill:#552222;}#mermaid-svg-WqXeFLhGGHmHOPBZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-WqXeFLhGGHmHOPBZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-WqXeFLhGGHmHOPBZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-WqXeFLhGGHmHOPBZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-WqXeFLhGGHmHOPBZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-WqXeFLhGGHmHOPBZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-WqXeFLhGGHmHOPBZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-WqXeFLhGGHmHOPBZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-WqXeFLhGGHmHOPBZ .marker.cross{stroke:#333333;}#mermaid-svg-WqXeFLhGGHmHOPBZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-WqXeFLhGGHmHOPBZ p{margin:0;}#mermaid-svg-WqXeFLhGGHmHOPBZ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-WqXeFLhGGHmHOPBZ .cluster-label text{fill:#333;}#mermaid-svg-WqXeFLhGGHmHOPBZ .cluster-label span{color:#333;}#mermaid-svg-WqXeFLhGGHmHOPBZ .cluster-label span p{background-color:transparent;}#mermaid-svg-WqXeFLhGGHmHOPBZ .label text,#mermaid-svg-WqXeFLhGGHmHOPBZ span{fill:#333;color:#333;}#mermaid-svg-WqXeFLhGGHmHOPBZ .node rect,#mermaid-svg-WqXeFLhGGHmHOPBZ .node circle,#mermaid-svg-WqXeFLhGGHmHOPBZ .node ellipse,#mermaid-svg-WqXeFLhGGHmHOPBZ .node polygon,#mermaid-svg-WqXeFLhGGHmHOPBZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-WqXeFLhGGHmHOPBZ .rough-node .label text,#mermaid-svg-WqXeFLhGGHmHOPBZ .node .label text,#mermaid-svg-WqXeFLhGGHmHOPBZ .image-shape .label,#mermaid-svg-WqXeFLhGGHmHOPBZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-WqXeFLhGGHmHOPBZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-WqXeFLhGGHmHOPBZ .rough-node .label,#mermaid-svg-WqXeFLhGGHmHOPBZ .node .label,#mermaid-svg-WqXeFLhGGHmHOPBZ .image-shape .label,#mermaid-svg-WqXeFLhGGHmHOPBZ .icon-shape .label{text-align:center;}#mermaid-svg-WqXeFLhGGHmHOPBZ .node.clickable{cursor:pointer;}#mermaid-svg-WqXeFLhGGHmHOPBZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-WqXeFLhGGHmHOPBZ .arrowheadPath{fill:#333333;}#mermaid-svg-WqXeFLhGGHmHOPBZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-WqXeFLhGGHmHOPBZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-WqXeFLhGGHmHOPBZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WqXeFLhGGHmHOPBZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-WqXeFLhGGHmHOPBZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WqXeFLhGGHmHOPBZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-WqXeFLhGGHmHOPBZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-WqXeFLhGGHmHOPBZ .cluster text{fill:#333;}#mermaid-svg-WqXeFLhGGHmHOPBZ .cluster span{color:#333;}#mermaid-svg-WqXeFLhGGHmHOPBZ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-WqXeFLhGGHmHOPBZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-WqXeFLhGGHmHOPBZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-WqXeFLhGGHmHOPBZ .icon-shape,#mermaid-svg-WqXeFLhGGHmHOPBZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WqXeFLhGGHmHOPBZ .icon-shape p,#mermaid-svg-WqXeFLhGGHmHOPBZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-WqXeFLhGGHmHOPBZ .icon-shape .label rect,#mermaid-svg-WqXeFLhGGHmHOPBZ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WqXeFLhGGHmHOPBZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-WqXeFLhGGHmHOPBZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-WqXeFLhGGHmHOPBZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-WqXeFLhGGHmHOPBZ .copy>*{fill:#5a3a3a!important;stroke:#ff8a80!important;color:#ffffff!important;}#mermaid-svg-WqXeFLhGGHmHOPBZ .copy span{fill:#5a3a3a!important;stroke:#ff8a80!important;color:#ffffff!important;}#mermaid-svg-WqXeFLhGGHmHOPBZ .copy tspan{fill:#ffffff!important;}#mermaid-svg-WqXeFLhGGHmHOPBZ .warn>*{fill:#7a5a1f!important;stroke:#ffd54f!important;color:#000000!important;}#mermaid-svg-WqXeFLhGGHmHOPBZ .warn span{fill:#7a5a1f!important;stroke:#ffd54f!important;color:#000000!important;}#mermaid-svg-WqXeFLhGGHmHOPBZ .warn tspan{fill:#000000!important;}#mermaid-svg-WqXeFLhGGHmHOPBZ .ok>*{fill:#1f4f1f!important;stroke:#66bb6a!important;color:#ffffff!important;}#mermaid-svg-WqXeFLhGGHmHOPBZ .ok span{fill:#1f4f1f!important;stroke:#66bb6a!important;color:#ffffff!important;}#mermaid-svg-WqXeFLhGGHmHOPBZ .ok tspan{fill:#ffffff!important;}#mermaid-svg-WqXeFLhGGHmHOPBZ .ng>*{fill:#7a1f1f!important;stroke:#ef5350!important;color:#ffffff!important;}#mermaid-svg-WqXeFLhGGHmHOPBZ .ng span{fill:#7a1f1f!important;stroke:#ef5350!important;color:#ffffff!important;}#mermaid-svg-WqXeFLhGGHmHOPBZ .ng tspan{fill:#ffffff!important;} +1 copy
+1 copy
+1 copy 录制时
+1 copy 编码时
允许
允许
允许
禁止
禁止
callback string_at
UvcCameraBackend.read
FrameWorker.snapshot
Recorder.write_frame
PNG/写盘 staging
整帧复制账本
单帧可达 5 次
一次性安全回退
同一 payload
重复复制
允许的复制 :C ABI callback 指针没有稳定生命周期时的安全回退;写盘/PNG/编码必须拥有独立 buffer 时的一次复制;小型 metadata 转为 bytes;用户主动请求的快照。
禁止的复制 :同一 payload 在 callback/GUI slot/recorder 入队/display publish 上重复复制;为了"线程安全"给 SHM view 加 Python Lock;把 4 MiB ndarray pickle 进 multiprocessing.Queue 或 Qt queued signal;在 GUI 线程做状态栏 reduction。
我们用的 token + 引用计数契约:
cpp
struct FrameToken {
uint64_t frame_id;
uint64_t generation; // 防止 ring wrap 后误读
uint32_t slot_index;
uint32_t payload_bytes;
uint32_t width, height, format;
uint64_t capture_ns;
};
struct FrameSlotHeader {
std::atomic<uint32_t> state; // FREE / WRITING / READY
std::atomic<uint32_t> ref_count;
uint64_t generation, frame_id;
uint32_t payload_bytes, metadata_bytes;
};
Python 侧只能调 acquire(token) / view(token) / release(token),不能直接改 state/ref_count 。如果不引入引用计数,至少给 display 和 recorder 各预留独立的 staging 双/三缓冲,并明确降级、把 copy_bytes 计入指标。
录制端再省一刀:
python
raw_file.write(memoryview(frame).cast("B")) # 0.005 ms vs tobytes 1.73 ms
约束:frame 必须是连续、小端、生命周期稳定;非连续或字节序不匹配时允许显式复制。
四、复合帧必须共享 frame_id
工业相机常传复合帧:1920×1080 图像 + 2 行原始信息行(带温度、状态、checksum)。最常见的 bug 是"GUI 拉一帧、统计延迟两秒、结果贴错帧"。
#mermaid-svg-kmQrO4x1OOcIm2k2{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-kmQrO4x1OOcIm2k2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kmQrO4x1OOcIm2k2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kmQrO4x1OOcIm2k2 .error-icon{fill:#552222;}#mermaid-svg-kmQrO4x1OOcIm2k2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kmQrO4x1OOcIm2k2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kmQrO4x1OOcIm2k2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kmQrO4x1OOcIm2k2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kmQrO4x1OOcIm2k2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kmQrO4x1OOcIm2k2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kmQrO4x1OOcIm2k2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kmQrO4x1OOcIm2k2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kmQrO4x1OOcIm2k2 .marker.cross{stroke:#333333;}#mermaid-svg-kmQrO4x1OOcIm2k2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kmQrO4x1OOcIm2k2 p{margin:0;}#mermaid-svg-kmQrO4x1OOcIm2k2 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-kmQrO4x1OOcIm2k2 .cluster-label text{fill:#333;}#mermaid-svg-kmQrO4x1OOcIm2k2 .cluster-label span{color:#333;}#mermaid-svg-kmQrO4x1OOcIm2k2 .cluster-label span p{background-color:transparent;}#mermaid-svg-kmQrO4x1OOcIm2k2 .label text,#mermaid-svg-kmQrO4x1OOcIm2k2 span{fill:#333;color:#333;}#mermaid-svg-kmQrO4x1OOcIm2k2 .node rect,#mermaid-svg-kmQrO4x1OOcIm2k2 .node circle,#mermaid-svg-kmQrO4x1OOcIm2k2 .node ellipse,#mermaid-svg-kmQrO4x1OOcIm2k2 .node polygon,#mermaid-svg-kmQrO4x1OOcIm2k2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-kmQrO4x1OOcIm2k2 .rough-node .label text,#mermaid-svg-kmQrO4x1OOcIm2k2 .node .label text,#mermaid-svg-kmQrO4x1OOcIm2k2 .image-shape .label,#mermaid-svg-kmQrO4x1OOcIm2k2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-kmQrO4x1OOcIm2k2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-kmQrO4x1OOcIm2k2 .rough-node .label,#mermaid-svg-kmQrO4x1OOcIm2k2 .node .label,#mermaid-svg-kmQrO4x1OOcIm2k2 .image-shape .label,#mermaid-svg-kmQrO4x1OOcIm2k2 .icon-shape .label{text-align:center;}#mermaid-svg-kmQrO4x1OOcIm2k2 .node.clickable{cursor:pointer;}#mermaid-svg-kmQrO4x1OOcIm2k2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-kmQrO4x1OOcIm2k2 .arrowheadPath{fill:#333333;}#mermaid-svg-kmQrO4x1OOcIm2k2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-kmQrO4x1OOcIm2k2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-kmQrO4x1OOcIm2k2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kmQrO4x1OOcIm2k2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-kmQrO4x1OOcIm2k2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kmQrO4x1OOcIm2k2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-kmQrO4x1OOcIm2k2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-kmQrO4x1OOcIm2k2 .cluster text{fill:#333;}#mermaid-svg-kmQrO4x1OOcIm2k2 .cluster span{color:#333;}#mermaid-svg-kmQrO4x1OOcIm2k2 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-kmQrO4x1OOcIm2k2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-kmQrO4x1OOcIm2k2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-kmQrO4x1OOcIm2k2 .icon-shape,#mermaid-svg-kmQrO4x1OOcIm2k2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kmQrO4x1OOcIm2k2 .icon-shape p,#mermaid-svg-kmQrO4x1OOcIm2k2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-kmQrO4x1OOcIm2k2 .icon-shape .label rect,#mermaid-svg-kmQrO4x1OOcIm2k2 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kmQrO4x1OOcIm2k2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-kmQrO4x1OOcIm2k2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-kmQrO4x1OOcIm2k2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-kmQrO4x1OOcIm2k2 .raw>*{fill:#3a3a5a!important;stroke:#9fa8da!important;color:#ffffff!important;}#mermaid-svg-kmQrO4x1OOcIm2k2 .raw span{fill:#3a3a5a!important;stroke:#9fa8da!important;color:#ffffff!important;}#mermaid-svg-kmQrO4x1OOcIm2k2 .raw tspan{fill:#ffffff!important;}#mermaid-svg-kmQrO4x1OOcIm2k2 .check>*{fill:#5a4a1f!important;stroke:#ffd54f!important;color:#000000!important;}#mermaid-svg-kmQrO4x1OOcIm2k2 .check span{fill:#5a4a1f!important;stroke:#ffd54f!important;color:#000000!important;}#mermaid-svg-kmQrO4x1OOcIm2k2 .check tspan{fill:#000000!important;}#mermaid-svg-kmQrO4x1OOcIm2k2 .img>*{fill:#1f4f7a!important;stroke:#4fc3f7!important;color:#ffffff!important;}#mermaid-svg-kmQrO4x1OOcIm2k2 .img span{fill:#1f4f7a!important;stroke:#4fc3f7!important;color:#ffffff!important;}#mermaid-svg-kmQrO4x1OOcIm2k2 .img tspan{fill:#ffffff!important;}#mermaid-svg-kmQrO4x1OOcIm2k2 .meta>*{fill:#4f7a1f!important;stroke:#aed581!important;color:#ffffff!important;}#mermaid-svg-kmQrO4x1OOcIm2k2 .meta span{fill:#4f7a1f!important;stroke:#aed581!important;color:#ffffff!important;}#mermaid-svg-kmQrO4x1OOcIm2k2 .meta tspan{fill:#ffffff!important;}#mermaid-svg-kmQrO4x1OOcIm2k2 .parse>*{fill:#7a4f1f!important;stroke:#ffb74d!important;color:#ffffff!important;}#mermaid-svg-kmQrO4x1OOcIm2k2 .parse span{fill:#7a4f1f!important;stroke:#ffb74d!important;color:#ffffff!important;}#mermaid-svg-kmQrO4x1OOcIm2k2 .parse tspan{fill:#ffffff!important;}#mermaid-svg-kmQrO4x1OOcIm2k2 .temp>*{fill:#7a1f4f!important;stroke:#f48fb1!important;color:#ffffff!important;}#mermaid-svg-kmQrO4x1OOcIm2k2 .temp span{fill:#7a1f4f!important;stroke:#f48fb1!important;color:#ffffff!important;}#mermaid-svg-kmQrO4x1OOcIm2k2 .temp tspan{fill:#ffffff!important;}#mermaid-svg-kmQrO4x1OOcIm2k2 .disp>*{fill:#1f3a5f!important;stroke:#7fb3ff!important;color:#ffffff!important;}#mermaid-svg-kmQrO4x1OOcIm2k2 .disp span{fill:#1f3a5f!important;stroke:#7fb3ff!important;color:#ffffff!important;}#mermaid-svg-kmQrO4x1OOcIm2k2 .disp tspan{fill:#ffffff!important;}#mermaid-svg-kmQrO4x1OOcIm2k2 .ng>*{fill:#7a1f1f!important;stroke:#ef5350!important;color:#ffffff!important;}#mermaid-svg-kmQrO4x1OOcIm2k2 .ng span{fill:#7a1f1f!important;stroke:#ef5350!important;color:#ffffff!important;}#mermaid-svg-kmQrO4x1OOcIm2k2 .ng tspan{fill:#ffffff!important;}#mermaid-svg-kmQrO4x1OOcIm2k2 .warn>*{fill:#5a4a1f!important;stroke:#ffd54f!important;color:#000000!important;}#mermaid-svg-kmQrO4x1OOcIm2k2 .warn span{fill:#5a4a1f!important;stroke:#ffd54f!important;color:#000000!important;}#mermaid-svg-kmQrO4x1OOcIm2k2 .warn tspan{fill:#000000!important;} OK
OK
旧 frame_id 拒绝
FAIL
raw payload
1920 × 1082 × 2
校验
payload_bytes
stride
endianness
image = rows 0:1080
info = rows 1080:1082
保留原始 bytes
versioned parser
temperature
frame_id=X
display
frame_id=X
不覆盖新帧
标记 metadata_error
图像仍可显示
图像和 metadata 必须用同一个 frame_id 对齐 ;旧结果不能覆盖新帧;解析失败只标记 metadata_error,图像仍可显示------绝不能"猜温度"。
这一条直接服务于 §三 的零拷贝契约:只有当 slot 把 image / infoline / metadata 放在同一 token 上、消费者按 frame_id 校验代际时,跨语义的"同一帧"才有保证。
五、OpenGL 上传:Intel 驱动黑屏与 PySide6 PBO 绑定坑
帧数据最终要落到屏幕。工业上位机常跑老 Intel 集显。GL_R16 / GL_R16UI 是 16-bit 单通道纹理最干净的接口,但实测在多个 Intel 驱动下上传后屏幕全黑。
#mermaid-svg-60QbP7PzenDJKvLy{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-60QbP7PzenDJKvLy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-60QbP7PzenDJKvLy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-60QbP7PzenDJKvLy .error-icon{fill:#552222;}#mermaid-svg-60QbP7PzenDJKvLy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-60QbP7PzenDJKvLy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-60QbP7PzenDJKvLy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-60QbP7PzenDJKvLy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-60QbP7PzenDJKvLy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-60QbP7PzenDJKvLy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-60QbP7PzenDJKvLy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-60QbP7PzenDJKvLy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-60QbP7PzenDJKvLy .marker.cross{stroke:#333333;}#mermaid-svg-60QbP7PzenDJKvLy svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-60QbP7PzenDJKvLy p{margin:0;}#mermaid-svg-60QbP7PzenDJKvLy .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-60QbP7PzenDJKvLy .cluster-label text{fill:#333;}#mermaid-svg-60QbP7PzenDJKvLy .cluster-label span{color:#333;}#mermaid-svg-60QbP7PzenDJKvLy .cluster-label span p{background-color:transparent;}#mermaid-svg-60QbP7PzenDJKvLy .label text,#mermaid-svg-60QbP7PzenDJKvLy span{fill:#333;color:#333;}#mermaid-svg-60QbP7PzenDJKvLy .node rect,#mermaid-svg-60QbP7PzenDJKvLy .node circle,#mermaid-svg-60QbP7PzenDJKvLy .node ellipse,#mermaid-svg-60QbP7PzenDJKvLy .node polygon,#mermaid-svg-60QbP7PzenDJKvLy .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-60QbP7PzenDJKvLy .rough-node .label text,#mermaid-svg-60QbP7PzenDJKvLy .node .label text,#mermaid-svg-60QbP7PzenDJKvLy .image-shape .label,#mermaid-svg-60QbP7PzenDJKvLy .icon-shape .label{text-anchor:middle;}#mermaid-svg-60QbP7PzenDJKvLy .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-60QbP7PzenDJKvLy .rough-node .label,#mermaid-svg-60QbP7PzenDJKvLy .node .label,#mermaid-svg-60QbP7PzenDJKvLy .image-shape .label,#mermaid-svg-60QbP7PzenDJKvLy .icon-shape .label{text-align:center;}#mermaid-svg-60QbP7PzenDJKvLy .node.clickable{cursor:pointer;}#mermaid-svg-60QbP7PzenDJKvLy .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-60QbP7PzenDJKvLy .arrowheadPath{fill:#333333;}#mermaid-svg-60QbP7PzenDJKvLy .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-60QbP7PzenDJKvLy .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-60QbP7PzenDJKvLy .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-60QbP7PzenDJKvLy .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-60QbP7PzenDJKvLy .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-60QbP7PzenDJKvLy .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-60QbP7PzenDJKvLy .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-60QbP7PzenDJKvLy .cluster text{fill:#333;}#mermaid-svg-60QbP7PzenDJKvLy .cluster span{color:#333;}#mermaid-svg-60QbP7PzenDJKvLy div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-60QbP7PzenDJKvLy .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-60QbP7PzenDJKvLy rect.text{fill:none;stroke-width:0;}#mermaid-svg-60QbP7PzenDJKvLy .icon-shape,#mermaid-svg-60QbP7PzenDJKvLy .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-60QbP7PzenDJKvLy .icon-shape p,#mermaid-svg-60QbP7PzenDJKvLy .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-60QbP7PzenDJKvLy .icon-shape .label rect,#mermaid-svg-60QbP7PzenDJKvLy .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-60QbP7PzenDJKvLy .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-60QbP7PzenDJKvLy .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-60QbP7PzenDJKvLy :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-60QbP7PzenDJKvLy .data>*{fill:#3a3a5a!important;stroke:#9fa8da!important;color:#ffffff!important;}#mermaid-svg-60QbP7PzenDJKvLy .data span{fill:#3a3a5a!important;stroke:#9fa8da!important;color:#ffffff!important;}#mermaid-svg-60QbP7PzenDJKvLy .data tspan{fill:#ffffff!important;}#mermaid-svg-60QbP7PzenDJKvLy .probe>*{fill:#5a4a1f!important;stroke:#ffd54f!important;color:#000000!important;}#mermaid-svg-60QbP7PzenDJKvLy .probe span{fill:#5a4a1f!important;stroke:#ffd54f!important;color:#000000!important;}#mermaid-svg-60QbP7PzenDJKvLy .probe tspan{fill:#000000!important;}#mermaid-svg-60QbP7PzenDJKvLy .ok>*{fill:#1f4f1f!important;stroke:#66bb6a!important;color:#ffffff!important;}#mermaid-svg-60QbP7PzenDJKvLy .ok span{fill:#1f4f1f!important;stroke:#66bb6a!important;color:#ffffff!important;}#mermaid-svg-60QbP7PzenDJKvLy .ok tspan{fill:#ffffff!important;}#mermaid-svg-60QbP7PzenDJKvLy .shader>*{fill:#1f4f7a!important;stroke:#4fc3f7!important;color:#ffffff!important;}#mermaid-svg-60QbP7PzenDJKvLy .shader span{fill:#1f4f7a!important;stroke:#4fc3f7!important;color:#ffffff!important;}#mermaid-svg-60QbP7PzenDJKvLy .shader tspan{fill:#ffffff!important;}#mermaid-svg-60QbP7PzenDJKvLy .out>*{fill:#1f3a5f!important;stroke:#7fb3ff!important;color:#ffffff!important;}#mermaid-svg-60QbP7PzenDJKvLy .out span{fill:#1f3a5f!important;stroke:#7fb3ff!important;color:#ffffff!important;}#mermaid-svg-60QbP7PzenDJKvLy .out tspan{fill:#ffffff!important;}#mermaid-svg-60QbP7PzenDJKvLy .ng>*{fill:#7a1f1f!important;stroke:#ef5350!important;color:#ffffff!important;}#mermaid-svg-60QbP7PzenDJKvLy .ng span{fill:#7a1f1f!important;stroke:#ef5350!important;color:#ffffff!important;}#mermaid-svg-60QbP7PzenDJKvLy .ng tspan{fill:#ffffff!important;}#mermaid-svg-60QbP7PzenDJKvLy .warn>*{fill:#7a5a1f!important;stroke:#ffb74d!important;color:#000000!important;}#mermaid-svg-60QbP7PzenDJKvLy .warn span{fill:#7a5a1f!important;stroke:#ffb74d!important;color:#000000!important;}#mermaid-svg-60QbP7PzenDJKvLy .warn tspan{fill:#000000!important;} RG8 OK
GL_R16 OK
都不行
Y16 frame
uint16
启动时探测
GL version
RG8 + shader
驱动白名单
GL_RG8 + UNSIGNED_BYTE
low→R high→G
fragment shader
重建 16-bit
查伪彩 LUT
screen
GL_R16 / GL_R16UI
Intel 黑屏 ⚠
CPU fallback
CPU LUT→RGBA staging
我们改用 GL_RG8 + GL_UNSIGNED_BYTE 双通道:把 little-endian Y16 的低/高字节分别送 R/G,fragment shader 内重建 16-bit 值再查伪彩 LUT。这条路在 Intel/NVIDIA/AMD 上都能跑。
PBO(pixel buffer object)异步上传在 OpenGL 教科书里几乎是"必须",但在 PySide6 6.11.1 的 glTexImage2D/glTexSubImage2D 绑定上有真实约束:
- 绑定的
pixels参数只接收 buffer 对象; None、0、ctypes.c_void_p(0)分别被拒绝或直接崩溃;- 不是配置问题,是绑定层根本不支持"null/整数偏移"惯用法。
python
# 不再是可选项:首次使用即永久回落直传
def _disable_pbo_upload(self):
self._pbo_enabled = False # glTexSubImage2D 直传是既定出货路径
CPU fallback 不是性能验收路径:必须单独记录帧率和 P95/P99,不能用它证明硬件 GPU 路径的预算达标。
六、NumPy LUT 与归一化的工程经验
GPU 拿到的是归一化后的 uint8 纹理。归一化本身是热路径,必须先在 NumPy 内优化再下沉 native:
python
# 65536 项 LUT(约 64 KiB),只在下限/上限变化时重建
codes = np.arange(65536, dtype=np.float32)
lut = np.clip((codes - lo) * (255.0 / (hi - lo)), 0, 255).astype(np.uint8)
# 预分配 gray8,禁止每帧创建
np.take(lut, frame_y16, out=gray8)
#mermaid-svg-Inb0gietvMqKXIHg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Inb0gietvMqKXIHg .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Inb0gietvMqKXIHg .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Inb0gietvMqKXIHg .error-icon{fill:#552222;}#mermaid-svg-Inb0gietvMqKXIHg .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Inb0gietvMqKXIHg .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Inb0gietvMqKXIHg .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Inb0gietvMqKXIHg .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Inb0gietvMqKXIHg .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Inb0gietvMqKXIHg .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Inb0gietvMqKXIHg .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Inb0gietvMqKXIHg .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Inb0gietvMqKXIHg .marker.cross{stroke:#333333;}#mermaid-svg-Inb0gietvMqKXIHg svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Inb0gietvMqKXIHg p{margin:0;}#mermaid-svg-Inb0gietvMqKXIHg .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Inb0gietvMqKXIHg .cluster-label text{fill:#333;}#mermaid-svg-Inb0gietvMqKXIHg .cluster-label span{color:#333;}#mermaid-svg-Inb0gietvMqKXIHg .cluster-label span p{background-color:transparent;}#mermaid-svg-Inb0gietvMqKXIHg .label text,#mermaid-svg-Inb0gietvMqKXIHg span{fill:#333;color:#333;}#mermaid-svg-Inb0gietvMqKXIHg .node rect,#mermaid-svg-Inb0gietvMqKXIHg .node circle,#mermaid-svg-Inb0gietvMqKXIHg .node ellipse,#mermaid-svg-Inb0gietvMqKXIHg .node polygon,#mermaid-svg-Inb0gietvMqKXIHg .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Inb0gietvMqKXIHg .rough-node .label text,#mermaid-svg-Inb0gietvMqKXIHg .node .label text,#mermaid-svg-Inb0gietvMqKXIHg .image-shape .label,#mermaid-svg-Inb0gietvMqKXIHg .icon-shape .label{text-anchor:middle;}#mermaid-svg-Inb0gietvMqKXIHg .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Inb0gietvMqKXIHg .rough-node .label,#mermaid-svg-Inb0gietvMqKXIHg .node .label,#mermaid-svg-Inb0gietvMqKXIHg .image-shape .label,#mermaid-svg-Inb0gietvMqKXIHg .icon-shape .label{text-align:center;}#mermaid-svg-Inb0gietvMqKXIHg .node.clickable{cursor:pointer;}#mermaid-svg-Inb0gietvMqKXIHg .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Inb0gietvMqKXIHg .arrowheadPath{fill:#333333;}#mermaid-svg-Inb0gietvMqKXIHg .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Inb0gietvMqKXIHg .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Inb0gietvMqKXIHg .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Inb0gietvMqKXIHg .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Inb0gietvMqKXIHg .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Inb0gietvMqKXIHg .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Inb0gietvMqKXIHg .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Inb0gietvMqKXIHg .cluster text{fill:#333;}#mermaid-svg-Inb0gietvMqKXIHg .cluster span{color:#333;}#mermaid-svg-Inb0gietvMqKXIHg div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Inb0gietvMqKXIHg .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Inb0gietvMqKXIHg rect.text{fill:none;stroke-width:0;}#mermaid-svg-Inb0gietvMqKXIHg .icon-shape,#mermaid-svg-Inb0gietvMqKXIHg .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Inb0gietvMqKXIHg .icon-shape p,#mermaid-svg-Inb0gietvMqKXIHg .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Inb0gietvMqKXIHg .icon-shape .label rect,#mermaid-svg-Inb0gietvMqKXIHg .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Inb0gietvMqKXIHg .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Inb0gietvMqKXIHg .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Inb0gietvMqKXIHg :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-Inb0gietvMqKXIHg .data>*{fill:#3a3a5a!important;stroke:#9fa8da!important;color:#ffffff!important;}#mermaid-svg-Inb0gietvMqKXIHg .data span{fill:#3a3a5a!important;stroke:#9fa8da!important;color:#ffffff!important;}#mermaid-svg-Inb0gietvMqKXIHg .data tspan{fill:#ffffff!important;}#mermaid-svg-Inb0gietvMqKXIHg .check>*{fill:#5a4a1f!important;stroke:#ffd54f!important;color:#000000!important;}#mermaid-svg-Inb0gietvMqKXIHg .check span{fill:#5a4a1f!important;stroke:#ffd54f!important;color:#000000!important;}#mermaid-svg-Inb0gietvMqKXIHg .check tspan{fill:#000000!important;}#mermaid-svg-Inb0gietvMqKXIHg .buf>*{fill:#4f7a1f!important;stroke:#aed581!important;color:#ffffff!important;}#mermaid-svg-Inb0gietvMqKXIHg .buf span{fill:#4f7a1f!important;stroke:#aed581!important;color:#ffffff!important;}#mermaid-svg-Inb0gietvMqKXIHg .buf tspan{fill:#ffffff!important;}#mermaid-svg-Inb0gietvMqKXIHg .lut>*{fill:#7a4f1f!important;stroke:#ffb74d!important;color:#ffffff!important;}#mermaid-svg-Inb0gietvMqKXIHg .lut span{fill:#7a4f1f!important;stroke:#ffb74d!important;color:#ffffff!important;}#mermaid-svg-Inb0gietvMqKXIHg .lut tspan{fill:#ffffff!important;}#mermaid-svg-Inb0gietvMqKXIHg .work>*{fill:#1f4f7a!important;stroke:#4fc3f7!important;color:#ffffff!important;}#mermaid-svg-Inb0gietvMqKXIHg .work span{fill:#1f4f7a!important;stroke:#4fc3f7!important;color:#ffffff!important;}#mermaid-svg-Inb0gietvMqKXIHg .work tspan{fill:#ffffff!important;}#mermaid-svg-Inb0gietvMqKXIHg .out>*{fill:#1f3a5f!important;stroke:#7fb3ff!important;color:#ffffff!important;}#mermaid-svg-Inb0gietvMqKXIHg .out span{fill:#1f3a5f!important;stroke:#7fb3ff!important;color:#ffffff!important;}#mermaid-svg-Inb0gietvMqKXIHg .out tspan{fill:#ffffff!important;}#mermaid-svg-Inb0gietvMqKXIHg .stats>*{fill:#7a1f4f!important;stroke:#f48fb1!important;color:#ffffff!important;}#mermaid-svg-Inb0gietvMqKXIHg .stats span{fill:#7a1f4f!important;stroke:#f48fb1!important;color:#ffffff!important;}#mermaid-svg-Inb0gietvMqKXIHg .stats tspan{fill:#ffffff!important;} 是
否
Y16 frame
1920×1080 uint16
LUT 缓存命中
且窗口未变?
预分配 gray8
重建 64 KiB LUT
一次 float32
np.take LUT
out=gray8
显示 / 编码
统计
1.5 s 节拍
保持原始 Y16 计数
几条经验:
- LUT 只在窗口变化时重建,禁止每帧重建;
gray8用固定预分配,禁止每帧np.empty/zeros/array;- raw Y16 录制完全绕过归一化,保留原始计数用于测温换算;
- 统计和归一化必须独立输出,禁止两个线程写同一 ndarray 区间;
Y16 >> 8虽然便宜,但丢动态范围、破坏低对比度显示,且后续温度换算直接算错------不允许作为优化手段;- 测温状态使用原始 Y16 计数的 min/max/mean,不能用右移后的 uint8 代入。
并行归一化:先测单 worker 的 np.take(out=...) P95;只有仍超预算才用 ThreadPoolExecutor(max_workers=2) 按行切成互不重叠区间。因为 NumPy native 操作通常释放 GIL,能拿到真并行;但若双 worker 没有降低 P95 就恢复单 worker------不能凭"多线程一定更快"下结论。
七、我们验证下来最稳的默认架构
把前面六节的所有设计拼起来,就是这张默认架构图:
#mermaid-svg-F8p4Qt9PlRV0QjeG{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-F8p4Qt9PlRV0QjeG .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-F8p4Qt9PlRV0QjeG .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-F8p4Qt9PlRV0QjeG .error-icon{fill:#552222;}#mermaid-svg-F8p4Qt9PlRV0QjeG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-F8p4Qt9PlRV0QjeG .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-F8p4Qt9PlRV0QjeG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-F8p4Qt9PlRV0QjeG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-F8p4Qt9PlRV0QjeG .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-F8p4Qt9PlRV0QjeG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-F8p4Qt9PlRV0QjeG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-F8p4Qt9PlRV0QjeG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-F8p4Qt9PlRV0QjeG .marker.cross{stroke:#333333;}#mermaid-svg-F8p4Qt9PlRV0QjeG svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-F8p4Qt9PlRV0QjeG p{margin:0;}#mermaid-svg-F8p4Qt9PlRV0QjeG .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-F8p4Qt9PlRV0QjeG .cluster-label text{fill:#333;}#mermaid-svg-F8p4Qt9PlRV0QjeG .cluster-label span{color:#333;}#mermaid-svg-F8p4Qt9PlRV0QjeG .cluster-label span p{background-color:transparent;}#mermaid-svg-F8p4Qt9PlRV0QjeG .label text,#mermaid-svg-F8p4Qt9PlRV0QjeG span{fill:#333;color:#333;}#mermaid-svg-F8p4Qt9PlRV0QjeG .node rect,#mermaid-svg-F8p4Qt9PlRV0QjeG .node circle,#mermaid-svg-F8p4Qt9PlRV0QjeG .node ellipse,#mermaid-svg-F8p4Qt9PlRV0QjeG .node polygon,#mermaid-svg-F8p4Qt9PlRV0QjeG .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-F8p4Qt9PlRV0QjeG .rough-node .label text,#mermaid-svg-F8p4Qt9PlRV0QjeG .node .label text,#mermaid-svg-F8p4Qt9PlRV0QjeG .image-shape .label,#mermaid-svg-F8p4Qt9PlRV0QjeG .icon-shape .label{text-anchor:middle;}#mermaid-svg-F8p4Qt9PlRV0QjeG .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-F8p4Qt9PlRV0QjeG .rough-node .label,#mermaid-svg-F8p4Qt9PlRV0QjeG .node .label,#mermaid-svg-F8p4Qt9PlRV0QjeG .image-shape .label,#mermaid-svg-F8p4Qt9PlRV0QjeG .icon-shape .label{text-align:center;}#mermaid-svg-F8p4Qt9PlRV0QjeG .node.clickable{cursor:pointer;}#mermaid-svg-F8p4Qt9PlRV0QjeG .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-F8p4Qt9PlRV0QjeG .arrowheadPath{fill:#333333;}#mermaid-svg-F8p4Qt9PlRV0QjeG .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-F8p4Qt9PlRV0QjeG .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-F8p4Qt9PlRV0QjeG .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-F8p4Qt9PlRV0QjeG .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-F8p4Qt9PlRV0QjeG .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-F8p4Qt9PlRV0QjeG .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-F8p4Qt9PlRV0QjeG .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-F8p4Qt9PlRV0QjeG .cluster text{fill:#333;}#mermaid-svg-F8p4Qt9PlRV0QjeG .cluster span{color:#333;}#mermaid-svg-F8p4Qt9PlRV0QjeG div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-F8p4Qt9PlRV0QjeG .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-F8p4Qt9PlRV0QjeG rect.text{fill:none;stroke-width:0;}#mermaid-svg-F8p4Qt9PlRV0QjeG .icon-shape,#mermaid-svg-F8p4Qt9PlRV0QjeG .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-F8p4Qt9PlRV0QjeG .icon-shape p,#mermaid-svg-F8p4Qt9PlRV0QjeG .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-F8p4Qt9PlRV0QjeG .icon-shape .label rect,#mermaid-svg-F8p4Qt9PlRV0QjeG .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-F8p4Qt9PlRV0QjeG .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-F8p4Qt9PlRV0QjeG .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-F8p4Qt9PlRV0QjeG :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-F8p4Qt9PlRV0QjeG .gui>*{fill:#1f3a5f!important;stroke:#7fb3ff!important;color:#ffffff!important;}#mermaid-svg-F8p4Qt9PlRV0QjeG .gui span{fill:#1f3a5f!important;stroke:#7fb3ff!important;color:#ffffff!important;}#mermaid-svg-F8p4Qt9PlRV0QjeG .gui tspan{fill:#ffffff!important;}#mermaid-svg-F8p4Qt9PlRV0QjeG .worker>*{fill:#3a5a3a!important;stroke:#9ccc65!important;color:#ffffff!important;}#mermaid-svg-F8p4Qt9PlRV0QjeG .worker span{fill:#3a5a3a!important;stroke:#9ccc65!important;color:#ffffff!important;}#mermaid-svg-F8p4Qt9PlRV0QjeG .worker tspan{fill:#ffffff!important;}#mermaid-svg-F8p4Qt9PlRV0QjeG .native>*{fill:#5a3a3a!important;stroke:#ff8a80!important;color:#ffffff!important;}#mermaid-svg-F8p4Qt9PlRV0QjeG .native span{fill:#5a3a3a!important;stroke:#ff8a80!important;color:#ffffff!important;}#mermaid-svg-F8p4Qt9PlRV0QjeG .native tspan{fill:#ffffff!important;}#mermaid-svg-F8p4Qt9PlRV0QjeG .opt>*{fill:#3a3a5a!important;stroke:#9fa8da!important;color:#ffffff!important;}#mermaid-svg-F8p4Qt9PlRV0QjeG .opt span{fill:#3a3a5a!important;stroke:#9fa8da!important;color:#ffffff!important;}#mermaid-svg-F8p4Qt9PlRV0QjeG .opt tspan{fill:#ffffff!important;} 可选
hcs_core.dll 同进程 C++ 真线程
唯一可见窗口
dirty + token
enqueue HIGH/NORM/LOW
record start/stop
FrameChannel
ctypes 释放 GIL
按需 SHM
hcs_client.exe
PySide6 GUI
FramePumpWorker
moveToThread
CommandWorker
唯一 owner
Recorder worker
可靠 raw
CaptureThread
USB 拉流
Parse/Normalize
Y16 + 信息行
AnalyticsPool
直方图/温度/LUT
无窗口 CPU worker
仅 GIL 重任务
各节点的设计来源:
- GUI ↔ FramePump 对应 §二的 dirty/token 流;
- FrameChannel 是 §三 的零拷贝总线,所有大帧都走 token;
- CaptureThread + ParseThread 共同保证 §四 的
frame_id对齐; - AnalyticsPool 与 §六 的归一化紧密耦合,归一化结果直接喂给 OpenGL;
- CommandWorker +
ctypes释放 GIL 是 §一 的具体落地; - 可选 CPU worker 的开关标准是 §七 末尾的"代价-收益"。
一个窗口、同进程 C++ 真线程、至多一个按需 CPU worker------这是我们验证下来性能、故障隔离和工程复杂度三者最均衡的位置。
八、跨语言硬约束 E1--E5
整套设计能否成立,取决于几条跨语言硬约束不被破坏。下图把每一类事故对应到原始规则:
#mermaid-svg-PKzhLDhEFukSrn1x{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-PKzhLDhEFukSrn1x .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PKzhLDhEFukSrn1x .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PKzhLDhEFukSrn1x .error-icon{fill:#552222;}#mermaid-svg-PKzhLDhEFukSrn1x .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PKzhLDhEFukSrn1x .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PKzhLDhEFukSrn1x .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PKzhLDhEFukSrn1x .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PKzhLDhEFukSrn1x .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PKzhLDhEFukSrn1x .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PKzhLDhEFukSrn1x .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PKzhLDhEFukSrn1x .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PKzhLDhEFukSrn1x .marker.cross{stroke:#333333;}#mermaid-svg-PKzhLDhEFukSrn1x svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PKzhLDhEFukSrn1x p{margin:0;}#mermaid-svg-PKzhLDhEFukSrn1x .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-PKzhLDhEFukSrn1x .cluster-label text{fill:#333;}#mermaid-svg-PKzhLDhEFukSrn1x .cluster-label span{color:#333;}#mermaid-svg-PKzhLDhEFukSrn1x .cluster-label span p{background-color:transparent;}#mermaid-svg-PKzhLDhEFukSrn1x .label text,#mermaid-svg-PKzhLDhEFukSrn1x span{fill:#333;color:#333;}#mermaid-svg-PKzhLDhEFukSrn1x .node rect,#mermaid-svg-PKzhLDhEFukSrn1x .node circle,#mermaid-svg-PKzhLDhEFukSrn1x .node ellipse,#mermaid-svg-PKzhLDhEFukSrn1x .node polygon,#mermaid-svg-PKzhLDhEFukSrn1x .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-PKzhLDhEFukSrn1x .rough-node .label text,#mermaid-svg-PKzhLDhEFukSrn1x .node .label text,#mermaid-svg-PKzhLDhEFukSrn1x .image-shape .label,#mermaid-svg-PKzhLDhEFukSrn1x .icon-shape .label{text-anchor:middle;}#mermaid-svg-PKzhLDhEFukSrn1x .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-PKzhLDhEFukSrn1x .rough-node .label,#mermaid-svg-PKzhLDhEFukSrn1x .node .label,#mermaid-svg-PKzhLDhEFukSrn1x .image-shape .label,#mermaid-svg-PKzhLDhEFukSrn1x .icon-shape .label{text-align:center;}#mermaid-svg-PKzhLDhEFukSrn1x .node.clickable{cursor:pointer;}#mermaid-svg-PKzhLDhEFukSrn1x .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-PKzhLDhEFukSrn1x .arrowheadPath{fill:#333333;}#mermaid-svg-PKzhLDhEFukSrn1x .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-PKzhLDhEFukSrn1x .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-PKzhLDhEFukSrn1x .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PKzhLDhEFukSrn1x .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-PKzhLDhEFukSrn1x .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PKzhLDhEFukSrn1x .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-PKzhLDhEFukSrn1x .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-PKzhLDhEFukSrn1x .cluster text{fill:#333;}#mermaid-svg-PKzhLDhEFukSrn1x .cluster span{color:#333;}#mermaid-svg-PKzhLDhEFukSrn1x div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-PKzhLDhEFukSrn1x .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-PKzhLDhEFukSrn1x rect.text{fill:none;stroke-width:0;}#mermaid-svg-PKzhLDhEFukSrn1x .icon-shape,#mermaid-svg-PKzhLDhEFukSrn1x .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PKzhLDhEFukSrn1x .icon-shape p,#mermaid-svg-PKzhLDhEFukSrn1x .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-PKzhLDhEFukSrn1x .icon-shape .label rect,#mermaid-svg-PKzhLDhEFukSrn1x .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PKzhLDhEFukSrn1x .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-PKzhLDhEFukSrn1x .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-PKzhLDhEFukSrn1x :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-PKzhLDhEFukSrn1x .py>*{fill:#1f3a5f!important;stroke:#7fb3ff!important;color:#ffffff!important;}#mermaid-svg-PKzhLDhEFukSrn1x .py span{fill:#1f3a5f!important;stroke:#7fb3ff!important;color:#ffffff!important;}#mermaid-svg-PKzhLDhEFukSrn1x .py tspan{fill:#ffffff!important;}#mermaid-svg-PKzhLDhEFukSrn1x .check>*{fill:#5a4a1f!important;stroke:#ffd54f!important;color:#000000!important;}#mermaid-svg-PKzhLDhEFukSrn1x .check span{fill:#5a4a1f!important;stroke:#ffd54f!important;color:#000000!important;}#mermaid-svg-PKzhLDhEFukSrn1x .check tspan{fill:#000000!important;}#mermaid-svg-PKzhLDhEFukSrn1x .ok>*{fill:#1f4f1f!important;stroke:#66bb6a!important;color:#ffffff!important;}#mermaid-svg-PKzhLDhEFukSrn1x .ok span{fill:#1f4f1f!important;stroke:#66bb6a!important;color:#ffffff!important;}#mermaid-svg-PKzhLDhEFukSrn1x .ok tspan{fill:#ffffff!important;}#mermaid-svg-PKzhLDhEFukSrn1x .ng>*{fill:#7a1f1f!important;stroke:#ef5350!important;color:#ffffff!important;}#mermaid-svg-PKzhLDhEFukSrn1x .ng span{fill:#7a1f1f!important;stroke:#ef5350!important;color:#ffffff!important;}#mermaid-svg-PKzhLDhEFukSrn1x .ng tspan{fill:#ffffff!important;}#mermaid-svg-PKzhLDhEFukSrn1x .ng2>*{fill:#7a1f1f!important;stroke:#ef5350!important;color:#ffffff!important;}#mermaid-svg-PKzhLDhEFukSrn1x .ng2 span{fill:#7a1f1f!important;stroke:#ef5350!important;color:#ffffff!important;}#mermaid-svg-PKzhLDhEFukSrn1x .ng2 tspan{fill:#ffffff!important;} 是
否
NumPy 视图
C++ 段
仍存活?
安全使用
悬垂访问
UAF / 撕裂
Python Lock
保护 C++ SHM
锁语义错配
跨进程/跨语言不成立
耗时 ctypes 调用
未释放 GIL
GUI 线程卡顿
重复 close / 半初始化
不幂等清理
句柄/SHM/view 残留
- E1:NumPy 视图生命周期不得超过 C++ 共享段 + slot lease。
- E2:ctypes 结构体、宽度、packing、常量集中定义并有 ABI 测试。
- E3 :C++ 内存只走 C ABI 原子原语 / FrameChannel 头尾,Python Lock 不能保护 C++ SHM。
- E4 :昂贵 native 调用必须释放 GIL,且不能从 Qt GUI 线程调用。
- E5:中断、半初始化、拔插、重复 close 都是正常输入;清理必须幂等。
- 设备 handle 单 owner :一个设备 handle 只能有一个串行 owner;
hcs_stop、callback drain、hcs_close必须按顺序完成。
E1/E3 是 §三 零拷贝契约的底线;E4 是 §一 / §二 线程拓扑能够成立的前提;E5 是 §七 默认架构在拔插、崩溃下仍然稳定的根因。任何一条被破坏,前面的所有优化都会立刻失效。