Files
MapLibreParticleLab/docs/20_调查/STEP-116_STEP113原PBF_FastPath阻塞安全修复审查_v1.md
tei c48a72a9d4
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
fix: guard STEP-113 empty annotation and expose PBF blockers
2026-10-11 11:50:03 +09:00

9.6 KiB
Raw Blame History

STEP-116 STEP-113 原 PBF Fast Path 阻塞安全修复审查 v1

日期:2026-10-11(JST)。结论:部分实现(PARTIAL);空 Annotation 生命周期受控修复已落源码,缺源和 Symbol R 准入继续 BLOCKED。没有新的设备 Fast 证据。

1. 当前事实与范围

仓库 /Users/tei/dev/MapLibreParticleLab,particle/main@e2d202b560ad0165101546597c43f2ef0ccf4542,Darwin 24.6.0 / x86_64。起点 tracked clean,只有 STEP-110/111 和本次 STEP-116 任务书未跟踪。版本仍 13.6.1-particle.23,不能用版本号相同推断 APK 源码相同。Integration main 80667d6c343411d03a760425438e3d292f452118 为用户提供的 RDC 核验事实,本轮只读相关规范,没有 fetch 或跨仓写入。

已读取 AGENTS、PARTICLE_BASELINE、项目规范全部八文件、总则 README 和四份有效总则、STEP-116 任务及 STEP-112/113/114/115 当前记录/报告和相关源码;Integration AGENTS、README、WORKFLOW、TEAM_MODEL、contracts、rfc、adr、instructions、status、compatibility 只读。本轮不使用子代理,不修改公开头文件、公开 API/ABI、Host/RN/JNI 控制协议、NavSea 或公网 Style。

2. 缺源 R:证据成立,空依赖准入不实施

保留文件 artifacts/maplibre-particle/android-validation/step113/source-change-after-full.json,SHA-256:

24a06fa1cb072a628c650723c58a880895ec656f4972fa55d3cb064bfb2aabda

Native 原层 index=2、ID=tide-spots、type=symbol、source=tide。R[0,63) 依赖 mapple/navsea_delivery/tide。Native 实际 Source 表是 mapple:raster、navsea_delivery:vector、org.maplibre.annotations:annotations、rn-weather-fields:raster-array、step113-source-probe:geojson;没有 tide。该文件属于旧 SOURCE_CHANGE 完整窗口,不冒充本次 STATIC_RC 实测。原记录中 fallback=unknown-dependency;当前 STEP-114 代码已提前报 missing-source。

RenderOrchestrator::retainedSegmentSourceState 在查 revision 前先检查缺源及 CustomMVTVector;普通 Vector 不走 CustomMVTVector 拒绝分支。renderSegmentDependencies 不因隐藏或当前桶为空删依赖。RenderSegmentRevisionBook::revisionFor 对未登记来源返回空值,保留删除墓碑用于同名重建;墓碑存在不能代替当前 Source 的 readiness。

源码链路审查:

链路 当前实现与限制
零贡献候选 createRenderTree 的 source/layer 匹配循环不会给缺源层生成 LayerRenderItem,updateList 默认 false;这支持“本轮没有对应新 RenderItem”,不等于全部旧 GPU 对象已完成禁用的证书
旧 GPU 生命周期 RenderLayer::markLayerRenderable 调用 activateLayerGroup 提交 ChangeRequest;Symbol 还处理 collisionTileLayerGroup,layerRemoved 停用两类 group。CPU 状态、待处理 requests、旧 buckets 与实际执行时点需要一起核验;无 source 的循环边界也不能只靠 bool 推断
Source 创建/删除/同名恢复 createRenderTree 的 sourceDiff 递增全局内容及对应 source revision;prepare 输入的 immutable sources 身份变化使 NonCameraSignature 不匹配,必须先 Full
同名更新/异步完成 changed sourceDiff、onTileChanged → TileMutation → 内容/source revision + observer invalidate;feature-state 同样走 source 变更,不能按 HTTP 下载或 loaded 时间代替
全局输入 layerDiff / 层序、imageDiff、Glyph URL/fontFaces、onGlyphsLoaded、Sprite/ImageMissing、context/style cleanup 保留原失效链

因此本轮没有把 missing-source 当 empty,没有登记虚构 tide revision,没有补假源或删除 tide-spots。后续需要“该 R 在成功 Full 中实际参与 GPU 绘制的 group/碰撞 group 已排除、无待应用资源变化”的有界证据,以及 source 出现/恢复后下一次尝试必定 Full 的回归。当前仅 Source 表缺失不足以承担这项新准入契约。服务器正式 Style 缺源仍是 BLOCKED。

本轮只在现有诊断 JSON 的每段增加可选 missingSourceIds,由同次 snapshot 的依赖和 Source 表计算,包含缺失来源的具体 ID。查询不写 revision、不构造缓存、不修改首错和 24 Gate。

3. 空 Annotation C:局部修复与保护

旧 STATIC_RC Native 66 层:原63层 + weather-color + raster-particle + 自动 points Symbol。C 从 rn-weather-color 开始,普通 Symbol 不支持 particle dynamic range,阻挡 #22,并影响聚合 #23。SOURCE_CHANGE 的额外 circle 也必须保留。

仅在 Step113PbfGateScene 的 STATIC_RC 第二次 Style 加载完成时执行一次空层调整:

  1. 生产基础 ID 不含 points,生产Sources不得占用org.maplibre.annotations;SDK map.annotations 必须为空;实际层必须是 SymbolLayer、sourceId 为 org.maplibre.annotations,内部 Source 必须存在。来源由本次原始JSON无内部ID、Native加载路径与实际层共同核对,避免把生产同名Source误当空SDK来源。
  2. 使用 Style.removeLayer(Layer) 对象重载,保留可重加的 wrapper;Source 保留,原63层定义/顺序及生产 Sources 原样。
  3. 记录 emptyBuiltinAnnotationLayerRemoved、实例数0、Source保留、移除层ID/作用范围,保存 productionSources;poll/export 继续使用真实 Native 计数。静态预期正常 STATIC_RC 65 层,不能当实际计数。
  4. 不周期删除任何层。后续普通 Style.addLayer 保持原行为,native immutable layer/signature/topology 变化继续 Full;Style 更换后不对新 Style 再做移除。
  5. stop 在相同 Style 且该 ID 仍缺失时重加原 wrapper,已有同名层不覆盖。Style 身份改变或 Annotation/points 重新出现时,3秒 poll 撤销场景验收条件并报 BLOCKED;poll 是报告保护,不承担恢复可见内容的时序保证。

关键修复位于内部 src/mln/annotation/annotation_manager.cpp/.hpp:原 updateStyle 只在 Source 不存在时创建 points,Source 保留而层被删除会让后续 Marker 无绘制层。提取私有 ensurePointLayer,独立检查 Source 和层;首次添加 SymbolAnnotation、更新已有 SymbolAnnotation、Style loaded 三个入口修复缺失层。更新没有 geometry/icon 变化时也把 dirty 置真。已有同名 Layer 不被覆盖。

时序:Map::addAnnotation / updateAnnotation → AnnotationManager 修复 Style layer vector → Map::Impl::onUpdate 捕获新的 immutable layers → Fast NonCameraSignature/annotationMutation 拒绝旧树 → Full 执行正常 Annotation 渲染。恢复不依赖3秒poll,也没有在 renderer thread 的 updateData 内重写 Style。Shape Annotation 原接口/图层仍按原逻辑处理,碰撞、Fade、Glyph/Image 和 C 判断完全保留。

风险:这是 Shared Core 内部生命周期修复,所有后端共用;需 RDC 编译并跑真实图像用例,确认无锁/重入问题及旧 Annotation 行为回归。用户主动提供同名层时尊重其语义;若其配置本身无法绘制,该情况不被当作 Test App 空层安全条件。恢复失败在日志保留,不写成功。

4. Symbol R:首次 Build 仍 BLOCKED

已读 PlacementController、Placement::placeLayers/commit/symbolFadeChange、RenderTreeImpl::prepare、SymbolBucket::updateVertices、renderer Full 准入和 scene 聚合。

Continuous Full 分支 当前 Symbol R gate
placement 不 recent placementChanged=true;placeLayers 后 commit、setPlacement,changed 本身阻断
placement recent setPlacementStale;hasTransitions 返回 true,阻断
transitions 关闭 placementIsRecent=false,Full changed=true;symbolFadeChange 返回1,不是 settled=0

RenderTreeImpl::prepare 在 bucket updateVertices 中更新 opacity/dynamic attributes,并置 uploaded/dynamicUploaded/placementChangesUploaded 等状态。Placement commit 只提交 CPU placement/opacity 结果,不证明当前 R 的最终 GPU 输入一致。per-R renderSegmentPlacementGatePassed 和 retainedSegmentIsStable,全局 retainedPlacementStable / retainedSupported / preparedSceneSupported 均保留。隐藏/缺源/当前 placementData 为空的 Symbol 仍结构上具备 Placement 能力。

需要的额外设计证据:成功 Full 对应的 GPU 上传/绘制完成代次、R 使用的 bucket/tiles/跨瓦片ID与排序、跨Source碰撞与所有参与碰撞层的 Placement结果、text/icon opacity和dynamic vertices、atlas/glyph/image代次、Style/Source/layer identities与revision、camera/projection/viewport/pixelRatio、uniform fade及debug/3D条件、待处理requests/异步变更纪元。证书必须绑定一次成功 Cache Build,变化即 invalid/Full,并对 GL/Metal 验证等价及 shadow。现有代码没有完整证书,本轮不做跨层重写,不用毫秒阈值、loaded、无手势或counter装配代替。

5. 测试源码与结论边界

新增四个 Step116Safety.* C++ 用例:缺源/Source加入未loaded/异步revision/删除墓碑和恢复;JSON缺源审计与Placement并存、查询无副作用;Symbol两个Continuous分支及关闭transition保护、无变化/Source与全局资源revision;Annotation/层序/disable/reload多失败 shadow 全集。revision用例验证的是账本契约,不伪造实际 Glyph/Tile 回调或 GPU完成。

新增三个 Annotations.Step116* 用例:空points移除后真实添加在render前恢复并比对既有point_annotation图像;已有Marker no-op update恢复且出图;已有同名层不覆盖、reload恢复且出图。这些是测试源码,全部 NOT RUN;设备的annotation/Style、async mutation、GL/Metal回退还须独立验收。

本轮实现后预期消除原 STATIC_RC 的空内部 C Symbol;无法承诺 #23 清除,也无法承诺 R Build/Fast>0。旧快照、AST或diff检查均不构成新制品安全、像素或性能 PASS。