Files
MapLibreParticleLab/docs/20_调查/STEP-096_V13隔离盲审调查_v1.md
tei b5c1c4879a
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
docs: deliver isolated blind V13 audit STEP-096
2026-09-25 21:31:27 +09:00

37 KiB
Raw Permalink Blame History

STEP-096 V13 隔离盲审调查 v1

本报告仅使用 /Users/tei/dev/MapLibreParticleV13Blind096 内的当前规范、任务书与源码。发现由本轮独立阅读、追踪调用链及反例检查形成;未读取外部仓库、其他轮次审查、历史报告、会话日志、网页或既有审查结论。未构建、未执行测试、未运行设备、未修改源码。

1. 输入、结论边界与证据等级

  • 合同:STEP-096 任务书。已读取根 AGENTS、PARTICLE_BASELINE、项目规范目录、总则 README 指定的当前有效文件。
  • 唯一目录:/Users/tei/dev/MapLibreParticleV13Blind096。目标导出 SHA:ee0b89e711bf01335b3aa71d36def785cd97fec3,来源为任务书声明;本轮没有 Git 对象可供独立核验,未运行 Git。branch、当前 HEAD、Git clean/remote 状态均为 NOT AVAILABLE。
  • release manifest 声明 particleRevision=13、sourceCommit=ddf20dec9fe9cd05b77274827b6825829500dd5a,RN/Android 为 13.4.1-particle.13,iOS 为 6.28.0-particle.13。manifest 的 sourceCommit 是产物来源声明,不能替代本次快照导出 SHA,也不能据此断言导出错误。
  • 当前基线总则仍包含 .3 发布坐标;本轮以快照源码及 .13 manifest 描述实现。此为文档口径差异,未据此推断线上产物错误。manifest 的发布成功字样不是本轮公网回读证据。
  • 当前工作机器观察值为 Mac-mini-tei.lan;工作日期为 2026-09-25。实际 TUI 启动命令、screen 归属、模型配置及 reasoning 设置的独立启动记录均 NOT AVAILABLE;不访问目录外补证,不声称核实了 RDC 启动要求。

“确定缺陷”指限定触发条件下源码控制流或数值关系已闭合,不表示已复现用户事故。“源码确定成本”指可证明的调用、遍历、分配或等待,不表示 profiler 排序。“设计限制”指当前能力或上界不足。所有 GPU 画面、耗时、卡顿、OOM、崩溃、性能收益均 NEEDS_RUNTIME;所有构建和测试均 NOT RUN。未发现可由本轮证据成立的 P0。

2. 独立架构地图

路径 实际责任和所有权 审查定位
RN RendererRuntime → 平台 bridge → Map/Renderer → RenderOrchestrator → segment plan Host 提交策略配置;Style 层顺序仍是绘制顺序来源,策略在该顺序上划分区间 RN 入口、分段计划、Renderer
Renderer → RetainedUnderlayCache → gfx RenderPass/OffscreenTexture → GL/Metal Renderer 持有各 retained 段缓存;缓存拥有离屏纹理/合成资源;同帧 PaintParameters 穿过多个目标 缓存生命周期、渲染调度
RN FieldRuntime → Android/iOS bridge → WeatherRuntime/MLNParticleFieldRuntime → RasterArraySource bridge 定位地图并派发;Source 持有 encoded store、绑定、forecast clock;发布不可变 Impl/投影供 RenderLayer 读取 RN API、Source 提交
encoded ingress → QBIN V2/V3 → profile validator → EncodedFieldStore/FieldSeries → binding/window magic 分派格式,校验结构和 profile;每个 fieldKey 保存时间序列;时间窗选取后物化数组 V3 解码、profile、series
projection → WeatherColorLayer / RasterParticleLayer → GL/Metal shader 色场与粒子分层;颜色消费标量或向量幅值;粒子持有场纹理、State A/B、Trail A/B 和 drawable/tweaker 物化、粒子更新、颜色更新

正常路径存在实际保护:Style diff 处理增删/重排;无效分段与不支持场景可 fallback;稳定 retained 帧可复用;解码有结构、尺寸、CRC 等检查;同时间身份冲突和陈旧修订会拒绝;超出时间覆盖范围不会凭空外推。Source 使用共享不可变帧/投影,使读者可持有旧快照;粒子 tweaker 使用 weak_ptr,不能仅因资源与 drawable 互相引用的表象断言泄漏。

固定边界的实现情况:正式 Host 提交路径未要求 Native 承担 provider/manifest 下载;旧 fixture/local FileSource 路径应单独理解。RN 是控制入口,绘制仍在 Native Core。粒子模块有独立 field/ingress/render 层,但 Renderer/RenderOrchestrator 中也存在较多集成逻辑;没有上游对照输入,无法量化“薄接入”变更比例。Wave/Tide/Current 的共享地理掩码要求见 B09。GL 与 Metal 不能由共享 C++ 自动推出行为一致;Vulkan 完整粒子链未在本轮闭合。

3. 独立发现总表

编号按本轮源码证实顺序,排除的候选不占编号;优先级是建议处理顺序,不是实测事故等级。

编号 优先级 类型 置信度 发现及作用边界
B01 P1 确定缺陷 高 切换离屏/主目标未重置 tile stencil 的 CPU 覆盖记录
B02 P1 确定缺陷 高 Metal RenderPass 没有落实 gfx 的 clearDepth/clearStencil 请求
B03 P2 源码确定成本 高 Metal 每次离屏 present 显式等待 GPU 完成
B04 P2 源码确定成本 高 retained 内容失效同时释放同尺寸 GPU 资源
B05 P2 源码确定成本 高 时间更新先全量物化场,再判断投影是否相同
B06 P1 确定缺陷 高 encoded PLAY 无生产路径推进 forecast clock
B07 P2 确定缺陷 高 8-bit 对称场编码不能表示零,产生有符号偏差
B08 P2 确定缺陷 高 Mercator 东向 U 位移缺少纬度修正因子
B09 P2 设计限制 高 当前掩码为合成 fixture,未形成共享地理 Ocean Mask
B10 P2 源码确定成本 高 平台 UI 派发链同步解码并物化 encoded 数据
B11 P2 设计限制 高 encoded store 仅限制每序列帧数,无全局字节/key 上界
B12 P2 确定缺陷 高 retained 段绘制抛异常后仍可能标记缓存构建成功
B13 P1 确定缺陷 高 粒子场更新门槛漏掉合法时间窗内的 V/有效性变化

B01 — stencil 覆盖记录没有随渲染目标切换

触发与调用链。 多个 retained 段,或 retained 构建后主目标绘制,复用相同瓦片集合且需要 stencil 裁剪。Renderer buildRetainedRange 共用 PaintParameters;beginBuild 创建清 stencil=0 的离屏 pass,GL 只更新 stencil 可用性;随后主 pass 也清 stencil=0。updateStencilBufferAvailability 不清覆盖表;tileIDsCovered 和早退 只看 tile ID,不看 attachment。下一目标仍会用旧的 Equal stencil ID。GL LayerGroup 正常调用掩码生成,也无法绕过这个早退。

所有权、正常/错误/销毁。 覆盖表属于同帧 PaintParameters,stencil 内容属于每个目标附件。下一帧重建 PaintParameters 不能修复同帧跨目标;显式 clearStencil 会重置记录,但上述目标切换没有调用。资源创建失败有 fallback,与本问题不同;正常创建成功即可触发。在 GL 已清为 0 的新目标上,旧正 ID 与实际内容不一致的路径已证实。Metal 的清屏另受 B02 影响。

反例与边界。 同一 attachment 内复用覆盖记录是正确优化;瓦片集合改变可能重绘掩码;无裁剪图层、没有跨目标构建或全部退回主绘制的帧不满足条件。不能据此称所有 retained 帧都缺图,实际画面 NEEDS_RUNTIME。

安全修复/最小验证。 在目标/attachment 世代切换时失效掩码元数据及 ID 分配状态,保留同目标复用;不可在已绘制内容中途随意清 stencil。构造 R/C/R 和 R/R 共用瓦片的 fill/line 层,逐目标检查掩码写入及截图,与关闭 retained 对照;含稳定复用、相机变化、主目标后续 live 层。

B02 — Metal 没有传递 depth/stencil 清屏语义

触发与证据。 retained beginBuild 明确请求 depth=1、stencil=0;Metal offscreen bind 新建 descriptor 并挂接 color/depth/stencil 纹理。Metal RenderPass 构造 只处理 clearColor,没有读取 clearDepth 或 clearStencil;GL 对应构造 把这些请求交给 clear。调用链为 Renderer → cache.beginBuild → gfx descriptor → Metal RenderPass → MTL descriptor,丢失发生在最后的转换。

所有权与反例。 附件属于 offscreen texture,pass/command encoder 管理本次命令。创建和销毁附件均不等于每次按请求清屏。主平台 descriptor 可自行配置清屏,不能证明新建离屏 descriptor 已正确配置;无 depth/stencil 的 pass 不受这两个字段影响。未使用外部 SDK 默认值推断确定黑屏,也未把未显式设置 StoreAction 直接定性为丢帧。模拟器 stencil attachment 分支另需设备验证。

影响/修复/验证。 确定的是 gfx 请求未落实,裁剪/深度表现 NEEDS_RUNTIME。统一转换各附件的 clear/load/store 语义,分别覆盖有无 clear 值、有无附件及模拟器组合格式。用非默认 depth/stencil clear 值做离屏后续比较测试,并核对 GPU capture;不得以 color 正常替代 depth/stencil 验证。

B03 — Metal 离屏 present 中存在同步等待

触发与链。 RenderTarget::render 在每个 target 绘制结束后 present;Metal command encoder 调 resource.swap;OffscreenTexture::swap commit 后执行 waitUntilCompleted,再重置命令缓冲。粒子稳态在目标注册 中至少包含 StateWrite 和 TrailWrite,初始化还有额外读目标;retained 构建也有 present。

所有权、边界和反例。 等待保护命令/资源完成,是确定同步点,不是已测出的长时间阻塞。仅实际执行这些目标的帧计入;停绘、未激活层不计,retained 稳定复用不计新的 cache build 等待,GL 不经过此 Metal 函数。完成/销毁安全不能通过简单删 wait 保证。

修复/最小验证。 研究同队列命令依赖与完成回调持有资源,合并或延后提交,保留读写顺序及销毁保护。Metal capture 记录单粒子层每帧 command buffer、wait 次数和 CPU 等待时长,再测多粒子层、首帧、retained 重建及移除层;性能收益 NEEDS_RUNTIME。

B04 — 内容失效与资源释放绑定

触发与链。 相机矩阵/内容 revision 改变而 viewport 尺寸不变。Renderer revision 更新 → cache evaluate → releaseResources → prepareResources 重新创建离屏资源。releaseResources 清掉纹理、合成组及资源键。

正常/错误/销毁与反例。 缓存对象持有资源,失效已实际释放所有权;稳定 valid 分支会 Reuse,不能称每帧都分配。viewport、格式、context 改变或最终销毁确需释放;资源创建异常已有 fallback。本 finding 针对同尺寸、同 context 的内容失效成本,不主张旧图继续有效。

修复/验证。 分开“内容有效性”和“附件可复用性”,保留匹配资源键的附件并重绘。固定 viewport 平移/缩放,对比 offscreen 创建次数、峰值显存、帧时间;再覆盖 resize/context 重建,防止错误跨 context 复用。成本路径已证实,分配耗时与收益 NEEDS_RUNTIME。

B05 — 时间更新重复物化整个场

触发与链。 Host 高频 setEncodedForecastTime,包括相同非零时间。setRequestedForecastTime → refreshV4RendererState;clock setter 不因同值返回 false;refresh 先物化两类投影,再用 sameProjection 比较。validity 展开 bitset,vectorSnapshot 遍历向量;颜色 UV 与粒子各有自己的 T0/T1 物化。sameProjection 还会比较实际数组。

所有权和反例。 新数组在临时共享快照中,比较相同后可释放;发布新 Impl 时旧读者继续持有旧快照。无投影、无绑定、无法解出窗口会少做或跳过相关分支。相同内容可避免发布或 GPU 重传,故不等于每次都上传纹理;每个动画帧也不一定调用该时间 setter。完整性校验是必要成本,问题是同内容重复重建。

成本/修复/验证。 N 个格点、两个时间面、颜色 UV 与粒子同时激活时,四份 vector+valid 基础数组约为 36N 字节的新逻辑存储,另有比较/校验遍历和旧快照重叠。缓存以完整不可变内容身份(含两分量、validity、grid、解释规则)为键,将 mix/time 与数组物化分离。统计同值时间、仅 mix 变化、跨窗、修订替换时的分配与上传次数;不得牺牲 B13 所需的完整身份。

B06 — encoded PLAY 没有接入 forecast clock 推进

触发与链。 提交两帧、绑定、设置时间后,经公开 API 切 PLAY 并等待 repaint。Source 播放设置及 advanceForecastClock 中,播放设置只改 clock 状态,推进由独立方法完成。对 src/include/platform/RN 生产路径检索 advanceForecastClock 仅有声明和定义,没有调用方。Android repaint timer 与 iOS timer 只触发重绘;粒子读取 v4 mix/cadence 不推进该 clock。

正常/错误/销毁与反例。 clock 由 Source 所有;平台 timer 的生命周期清理不提供时间增量。advance 实现 本身存在且处理溢出;测试 直接手动调用,所以不能证明公开 PLAY 可自行推进。Host 持续设置 forecast time、C++ 调用方自行 advance、旧 sourceTimeline 路径均是反例;粒子在固定场中移动也不代表预报时刻在播放。

修复/验证。 为 encoded clock 指定唯一推进所有者和单调时钟来源,处理暂停/恢复、rate、地图移除、颜色单层及多层,避免每层重复推进。仅调用一次 setTime 和 PLAY,观察 requestedForecastTime/mix 随 elapsed 改变;暂停保持、恢复不跳跃、不同 rate 成比例,窗口末端行为明确。公开路径缺口可静态确定,运行表现 NEEDS_RUNTIME。

B07 — 零值在 8-bit 场纹理中变成负值

触发与证据。 有效向量为零或接近抵消。粒子编码 用 uint8 转换 (x / scale * 0.5 + 0.5) * 255,scale 至少为 1;颜色编码 同类处理;GL 解码 还原为 (channel * 2 - 1) * scale,Metal 解码 同理。x=0 被截为 127,解码为 -scale/255,两个零分量都带偏差,最小 scale=1 时向量长度也高于 1e-5 的近零阈值。

边界和反例。 场快照的 CPU float 正确不代表上传表示正确;无效点被 valid 分支拒绝,不计入这个有效零场问题。不同数值/缩放下误差相对大小不同;方向归一化可放大近零偏差。理论截断误差小于 2*scale/255,这是假定合法编码区间的算术界,不是 GPU 实测。重用或销毁纹理不会改变表示误差。

修复/验证。 选取可精确表达零的带符号编码或 float/half 表示,并评估纹理兼容性;仅改四舍五入仍无法让奇数 255 的对称映射精确表示零。以有效零场、微小速度、正负对消、纯东/北场做 GPU 解码读回和粒子位移检查,GL/Metal 同测。实际漂移像素/速度 NEEDS_RUNTIME。

B08 — Mercator 东向位移少一个 sec(latitude)

触发与链。 formal profile 使用 eastward/northward、m/s 的 U/V 语义,见profile 校验。上传缩放在 shader 中还原后,GL formal 位移 取 dx=U*dt/C、dy=-V*dt/(C*cos(latitude));Metal 同样处理。这里 C 为地球周长,输入链未见对 U 预乘 sec(latitude)。

推导、反例和边界。 Mercator x=λ/(2π)+0.5,而局部东向速度满足 dλ/dt=U/(Rcosφ),故 dx 应为 Udt/(C*cosφ)。赤道、U=0 时不受此项影响;60° 纬度当前东向投影位移为应有值的一半。统一 visual time scale 不修复东西/南北相对畸变。此为固定输入语义下的数学缺陷,实际视觉速度、极区处理 NEEDS_RUNTIME;与资源销毁和错误 fallback 无关。

修复/验证。 在一致纬度与极区夹紧策略下对两个分量使用正确度量转换,保留既定视觉时间倍率。0°/45°/60° 的等物理速度东西向和南北向场,比较 CPU 解析预测与单步 GPU 位移;含符号、反向、极区边界与跨日期变更线。

B09 — Ocean Mask 尚未形成正式共享地理输入

触发与证据。 Wave/Tide/Current 需要 OCEAN_ONLY,或启用当前 mask 模式。makeCoastalMaskFixture 用格点坐标表达式生成合成海岸,formal 资源初始化仍安装该 fixture。物化统一写 GLOBAL。顶点 shader 的 v_world 源自 quad 坐标,粒子合成据此采样 mask;颜色分支 在该处理前返回。

所有权、正常路径与反例。 mask 纹理由粒子资源拥有,随资源重建/销毁;缺少正式地理 mask 数据入口和两类层共享采样语义。默认关闭 mask 的 GLOBAL wind 不因此错误,Host 数据 nodata 也可能已有陆地缺测,但这不等价于独立共享 Land/Ocean Mask。不能把本限制扩展为所有风场不可用。

修复/验证。 由 Host 决定业务域策略,Core 接收明确地理坐标、网格和版本的共享掩码,两层统一采样;不把 provider/下载引入 Native。加载真实小型固定海岸 mask,平移、缩放、旋转后地理边界应固定;分别验证 Wind GLOBAL 与 Wave/Current OCEAN_ONLY,以及 nodata 叠加、删除与替换。当前设计缺口已证实,设备画面 NEEDS_RUNTIME。

B10 — encoded 提交在 UI 派发链同步完成重工作

触发与链。 RN 提交较大 QBIN。Android bridge 经 runOnUiQueueThread 定位并调用 runtime;Java submit → JNI 拷贝 bytes 并同步调用 Source。iOS withMapForTag 使用 UI block → runtime submit 拷贝并解码。V3 CRC 对字节做位循环,后续解码/校验/投影也在同步调用链内。

边界、错误与反例。 Android 本地文件读取在 UI 派发之前,不能笼统写成全部文件 IO 都在 UI;小文件和 Duplicate 后的工作量不同,格式失败会提前返回。解码有大小上限,但允许的输入仍可较大。临时 bytes、decoded frame、Source 持久状态的所有权清楚;问题是所在线程及连续占用,并非已证明泄漏。未观测 ANR 或卡顿。

修复/验证。 worker 只做不可变 bytes 解码/校验,带 map/style/source 世代令牌返回所有者线程提交;限制队列并处理取消、地图销毁和过时结果,不能把整个可变 Source 移到 worker。在中小/近上限输入、CRC 失败、连发及页面退出下记录 UI 区间耗时、峰值内存和错误回调。性能影响 NEEDS_RUNTIME。

B11 — 每序列八帧不是全局内存预算

触发与链。 单 Source 连续提交不同有效 fieldKey。EncodedFieldStore 成员 是 map 和 perSeriesCapacity=8;submit 可为新 key 创建 series;FieldSeries 只检查本序列容量。Source 提交 不要求该 key 已绑定。V3 输入上限 限单帧 encoded/decoded bytes,不限 store 总量。

所有权和反例。 store 持有共享帧,直到显式 clear/clearAll 或 Source 销毁;渲染读者可能延长旧快照寿命。单帧 decoded 上限 32 MiB 与八帧组合形成每序列最高 256 MiB 的逻辑上界,但不表示每个 profile 都能达到该值。key 数无总上界,不能据此保证 Source 总内存。Host 固定少量 key 并主动清理时可控;本轮没有 OOM 证据。

修复/验证。 在提交前计入全局 key/字节预算,包含共享去重与临时峰值;明确容量拒绝语义,保护当前绑定窗口,避免静默淘汰。循环提交多个合法 key,验证达到预算后稳定拒绝且现有窗口可用;clear 后可恢复,地图销毁后所有者释放,读者完成后最终释放。设计上界不足已证实,实际内存峰值 NEEDS_RUNTIME。

B12 — 构建过程错误没有阻止 retained 缓存成功提交

触发与链。 retained 范围内某 LayerGroup 在 render 时抛出被捕获的异常,observer 记录错误后正常返回。RenderOrchestrator 遍历 捕获异常并 onRenderError,不把失败状态返回调用方。buildRetainedRange 在 opaque/translucent 遍历后继续 finishBuild、markBuildSucceeded;成功标记 使其可在后续稳定帧 Reuse。

所有权、错误/销毁与反例。 缓存持有可能只完成部分绘制的纹理,直到下次失效或销毁。资源准备阶段有 catch/fallback,不属于本路径;无异常、observer 再抛出异常、随后 revision 立刻改变都不满足持续复用条件。此处只讨论同步可捕获 render 异常,不将异步 GPU 错误视作同一证据。

修复/验证。 让范围绘制返回成败或设置本次构建错误标记,失败不提交 valid 缓存,安全 fallback/下帧重试;保留 observer 通知,不引入每帧 GPU 等待作为错误检测。用一次性抛异常的假 LayerGroup 和正常返回的 observer,检查 BuildFailed、禁止 Reuse、下一帧成功恢复,再由 GL/Metal 场景验证画面。

B13 — 粒子更新身份没有覆盖第二分量和有效性

触发与可构造输入。 同 fieldKey 的三个不同 validTime 帧使用相同 fieldID/generation/dataVersion,U 内容相同、V 依次变化(或 validity 变化),网格和 schema 兼容。此输入在FieldSeries 新时间插入 合法:newerRevision 检查针对同一 timestamp,未要求不同时间递增 generation。窗口从 T0/T1 切到 T1/T2。

完整调用链。 materializeParticleProjection 仅以 t0/t1FirstPlane 身份传给 baseSnapshot;baseSnapshot 用 fieldID+该身份组成 fieldID,保留 producer generation/dataVersion。Source sameProjection 比较实际数组,可正确发布新的 V/validity。但粒子 sourceChanged 只调用fieldIdentityChanged,其只比 generation/fieldID/dataVersion。两个槽身份都未变时,不进入内部 sameFieldGpuContent/纹理更新分支,继续使用旧场纹理。

所有权、反例和边界。 Source 新投影存在、Renderer 仍持有旧 resources.field/nextField 及纹理,直到其他身份变化、重建或销毁。每时次主动递增 generation、U 也变化、初次资源创建都能触发更新;纯 mix 变化且窗口内容确实不变应保留纹理。此 finding 不指向颜色层:颜色投影的实际内容比较是反例,已排除“颜色仅看 U 而漏掉 V”的候选。

修复/最小验证。 粒子更新门槛使用包含 U/V、validity、grid 与解释语义的完整不可变内容身份;窗口时间状态单独更新,保持相同内容可复用。采用上述三帧并让 V/validity 可见地不同,seek 跨窗,检查 CPU 选窗、GPU 上传/复用判定及读回;增加不同时间同 generation、仅 V 改变、仅有效性改变、完全相同内容四组。错误门槛可静态证明,旧场画面 NEEDS_RUNTIME。

4. 反例检查及未成案候选

  • 颜色投影身份:排除。 Source sameProjection → sameSnapshot → sameFieldGpuContent 会比较两分量/validity 实际数组;不能仅因第一 plane 参与 fieldID 就断言颜色不更新。B13 另有粒子特定的前置门槛证据。
  • 粒子资源强引用环:排除。 ValidationDrawableTweaker 持 weak_ptr;layerRemoved 移除目标。本轮未形成泄漏闭环。
  • 关闭缓存后辅助段状态残留:排除简单结论。 synchronizeRetainedUnderlayState 覆盖段缓存同步;不能仅查主缓存 setter。
  • 纯重排未被处理:排除。 Style diff 与orchestrator 更新 处理移除/添加并重建有序层;分段不是独立 Z-order 数据源。
  • 任意交错不受支持:排除笼统结论。 单 continuous 段的 fast path 限制不等于普通 segmented path 不支持多段;普通绘制 仍按计划遍历。透明混合、深度与遮挡整体等价性尚需运行验证。
  • 异常输入必然越界:未成案。 QBIN 有 MaxCells、字节预算、偏移/结构/CRC 检查,FieldSeries 有冲突/陈旧/兼容性拒绝。未完成形式化证明或 fuzz,不能声称完全安全,也未凭大输入符号制造越界 finding。

5. 性能与资源模型(静态模型,非 benchmark)

设视口为 W×H,retained 段数 K,格点数 N,活跃粒子层数 L,每层有效粒子数 P,encoded 字节数 E,fieldKey 数 F。

路径 静态成本模型 稳定路径/限制
retained 每段至少一张 RGBA8 颜色纹理约 4WH 字节,另加实际 depth/stencil 附件、对齐及 drawable;总量随 K 线性增长 稳定内容复用;Build 时绘制该段并合成;B04 增加同尺寸分配抖动
粒子 State A/B 随状态纹理面积增长,Trail A/B 随视口面积增长;积分和粒子绘制约随 P 增长,全屏衰减/合成随 WH 增长 初始化/重播种还有额外工作;不可只凭 particleCount 推断全部成本
Metal 提交 实际执行的粒子层稳态至少两次离屏同步点/层,加当帧 retained Build 数量及其他 target 这是显式 wait 次数下界模型,不是每次都发生长等待;需 capture 核对实际启用分支
时间 setter 激活投影物化和数组相等比较为 O(N),颜色 UV+粒子约 36N 字节基础新数组 单纯 draw 并不必然调用 setter;不同 scalar/vector 绑定成本不同
QBIN CRC 和解码大体随 E/样本数增长;提交链还会临时复制并可能物化 上限约束单帧;失败可以提前结束,不可套用最坏值到所有输入
encoded store 每 key 最多八帧,但总量随 F 增长;另有 materialized snapshot、上传图像、在途旧读者 需要显式 clear;现有容量拒绝保护单序列,未构成全局预算

未取得 profiler、锁竞争、allocator、GPU capture 或设备温度数据。不提供热点排名、FPS、毫秒、节省比例或确定 OOM 阈值。查到同步等待和 UI 同步调用,不等于证明死锁;本轮未形成有闭环证据的锁死 finding。

6. 测试源码覆盖与缺口

阅读测试源码只表示理解其断言,以下全部 NOT RUN。

已阅读的代表性测试 能说明的设计意图 不能替代的验证
retained 状态测试、失败测试、segment 测试 Build/Reuse/Fallback、范围与交错计划 真实 attachment stencil、Metal 清屏、异常经 orchestrator 传递、透明/深度像素等价
clock 测试、Host API clock 手动 advance 的时间算法 平台 PLAY 是否接入生产推进者
materialization 测试、颜色幅值 快照不变性、mix、重建计算 GPU 8-bit 数值误差、B13 跨窗同 producer 身份、数组分配次数
binding 测试、V3 解码测试 内容身份/选面、格式失败和边界 全 renderer 资源切换、UI 延迟、全局 key 预算及竞态销毁

未覆盖或未闭合:Vulkan/WebGPU 完整粒子实现、所有 Style 图层组合、真实设备 Metal simulator/device 差异、GPU 厂商驱动行为、平台业务调用频率、RN 安装/codegen/ABI、所有 error string 和 promise 回调分支、跨线程竞争注入、完整 fuzz、产物字节与 manifest 的对应关系。当前审查是有边界的静态追踪,不是逐行穷尽全仓或安全证明。

7. 后续可执行验证矩阵(交给 RDC,全部 NOT RUN)

本轮禁止执行以下构建/测试。RDC 在独立获准的验证阶段准备测试 runner 与设备;新增回归用例/修复属于后续代码任务,不能把下表当作本轮已提供可运行补丁。

项目 准备与执行动作 最小通过条件
B01 GL、Metal 各加载同瓦片 fill/line,策略 R/C/R、R/R、C/R/C;首帧/重建/复用逐帧对照关闭缓存 attachment 切换均生成对应掩码,像素裁剪等价
B02 离屏附件预置非默认值,创建带 depth/stencil clear 的 pass,再以比较绘制检查;device/simulator 各一次 clear 值与请求一致,无依赖未定义旧内容
B03/B04 1/2 粒子层、1/3 retained 段;固定视口平移 300 帧,记录 capture/分配/CPU 等待 给出次数、时长分布;资源复用变化有证据且无画面回归
B05 两时间面,固定时间重复 100 次、mix 小步变化 100 次,再跨窗和修订 输出物化/分配/上传次数;相同内容复用且变化可靠可见
B06 仅颜色、仅粒子、两者共存;setTime 一次后 PLAY、PAUSE、不同 rate forecast time/mix 正确推进,每地图只推进一次
B07 有效零场、微值、正负对消、东西南北常量场,GPU 单步读回 零保持零,近零阈值一致,误差有明确预算
B08 0°/45°/60°,相同 m/s、相同 dt,比较解析 Mercator 位移 U/V 投影度量一致,符号正确,极区有限值
B09 同一固定地理 mask 同时供颜色/粒子,平移缩放旋转,切 GLOBAL/OCEAN_ONLY 两层海陆边界一致且固定在地理位置
B10 合法中小/近上限 QBIN、坏 CRC、连发、加载中移除地图 UI 耗时可量化;回调一次;无过时提交/释放后访问
B11 多 key 连续提交直至预算,clear 后再提交 可预测拒绝、峰值有界、当前窗口不破坏、最终释放
B12 假 LayerGroup 首次抛异常,observer 记录后返回,下一帧正常 首次不得缓存成功;下一帧重试并恢复
B13 同 generation/dataVersion/U 的三时次,分别仅改 V、validity,再 seek 跨窗 新窗口 GPU 内容正确;完全相同内容仍可复用
兼容 增删/重排/隐藏边界层,透明层交错、3D/custom fallback、resize/context 重建 Style 顺序一致,fallback 可恢复,无残留目标

已有 C++ 定向测试的执行入口需使用平台生成的实际 runner;test/CMakeLists 的 mbgl-test 是库,macOS 配置 定义 mbgl-test-runner。RDC 已构建并确认路径后可执行下列命令(本轮未执行;路径必须由 RDC 填写):

RDC_TEST_RUNNER=/absolute/path/to/mbgl-test-runner
"$RDC_TEST_RUNNER" --gtest_filter='Retained*:ContinuousLayerRange.*:RenderSegment*:ParticleFastPath.*:ParticleField*:ParticleQbin*:ParticleEncoded*:Source.RasterArrayV4*'

这只运行现有匹配测试,不覆盖全部上述新增场景。RDC 应先确认 runner 的过滤输出确实命中测试,保存命令、exit code、测试数量和日志路径;设备场景记录设备/后端/版本、输入 fixture、截图或 capture。实际构建目录、签名、设备 ID、安装产物及历史日志均 NOT AVAILABLE,本轮不臆造构建命令或成功状态。

8. 分批建议与停止边界

  1. 先补正确性闭环: B01/B02 的目标附件状态、B06 的唯一时钟推进者、B13 的完整内容身份;加入最小回归后再做跨后端运行验证。B12 同批修复失败提交语义,避免错误缓存长期复用。
  2. 再统一数值与域语义: B07/B08 的数值表示/投影度量,以及 B09 的共享地理掩码;用解析输入和小型固定 fixture 定量验收。
  3. 最后按测量做性能与预算: B03/B04/B05/B10/B11,先记录同步、分配、线程耗时和峰值,再分别改命令提交、资源复用、物化缓存、工作线程和容量策略;避免多项同时修改而无法归因。

本轮仅交付三份中文盲审文档,不修改实现或测试。完成后停止,由 RDC 冻结三份原稿 SHA-256 和时间,再独立移交、追加开发记录并做正式仓库 scoped 提交及远端核验。本会话不参与解除盲态比较,不根据外部材料回写发现。

配套:Step 记录 · 完成报告