PySide6 工业上位机的实时帧链、零拷贝与跨语言架构

PySide6 工业上位机的实时帧链、零拷贝与跨语言架构

适合读者:正在用 PySide6 写工业控制、相机或数据采集上位机;想理解"Qt 主线程真的只能有一个,但 GIL 不是性能末日";被 4 MiB/帧的大块数据复制、OpenGL 上传黑屏、PyInstaller 产物臃肿反复折磨;在做 Python/C++ 混合架构,想搞清楚零拷贝究竟在哪里剪。

主线有三条:线程与背压、零拷贝与跨语言边界、打包与解释器。每一节按「原理→证据→方案→代价」组织。


一、PySide6 的"主线程只有一个"不等于"只能有一个真线程"

PySide6 的事件循环确实只在 GUI 主线程,但 Python 的 QThreadthreading.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 FramePumpQObject 放到长驻 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 对象;
  • None0ctypes.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 计数

几条经验

  1. LUT 只在窗口变化时重建,禁止每帧重建
  2. gray8 用固定预分配,禁止每帧 np.empty/zeros/array
  3. raw Y16 录制完全绕过归一化,保留原始计数用于测温换算;
  4. 统计和归一化必须独立输出,禁止两个线程写同一 ndarray 区间;
  5. Y16 >> 8 虽然便宜,但丢动态范围、破坏低对比度显示,且后续温度换算直接算错------不允许作为优化手段;
  6. 测温状态使用原始 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 是 §七 默认架构在拔插、崩溃下仍然稳定的根因。任何一条被破坏,前面的所有优化都会立刻失效。

相关推荐
PTCCTP1 小时前
CSP2024-J T3小木棍
c++
tachibana21 小时前
把RAGAS跑起来
数据库·人工智能·ai·架构·大模型·llm·rag
云深处@2 小时前
【C++设计模式】命令模式
c++·设计模式·命令模式
wuminyu2 小时前
JDK21 FFM异步读取MSG_ZEROCOPY错误队列揭秘
java·linux·c语言·jvm·c++
用户6919026813392 小时前
Agent 上下文记忆原理
设计模式·架构·设计
刘立军2 小时前
中心化配置与 I18n:严格禁止 AI 魔法值与硬编码参数
人工智能·后端·架构
江安下小雨2 小时前
muduo网络库(十三):Buffer 缓冲区类
c++·经验分享
两万五千个小时2 小时前
DeepSeek Harness 从 0 开始:18 todo 任务记忆
人工智能·程序员·架构
两万五千个小时2 小时前
DeepSeek Harness 从 0 开始:17 plan(计划模式)
人工智能·程序员·架构