Files
MapLibreParticleLab/docs/20_调查/R0_Placement跨段归因与缓存安全边界调查_v1.md
tei cdb45c023b
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
docs: investigate STEP-110 R0 placement attribution
2026-10-10 16:17:16 +09:00

13 KiB
Raw Permalink Blame History

R0 Placement 跨段归因与缓存安全边界调查 v1

STEP-110,2026-10-10;状态:PARTIAL(只读调查,非修复验收)。

执行记录:Codex CLI 0.161.0 实际会话模型 gpt-6-astra,推理 high,完成源码和历史证据审查;其报告写入阶段重复无响应,故本报告由 GPT 指挥 RDC 根据 Codex 调查结论独立回读关键源码后落盘。未运行测试、编译、ADB,也未修改业务实现。所有源码行号以调查起点 particle/main 2df504c 为准。

1. 发行源码核对

来源 Git 提交 实际意义
V23 GSRL Source Lock 87f3d1c59a RN / Android Native 13.6.1-particle.23 构建源码
V23 Tag 剥离后提交 a0a4787b43 构建提交之后的发行资料提交;注释 Tag 对象本身是 3ce42184…
当前 particle/main 和 origin/particle/main 调查起点 2df504cdc0 包含后续 STEP-109 尚未发行的 FieldSnapshot 比较优化

Codex 通过 Git Blob 对照确认:src/mln/renderer/render_orchestrator.cpp、renderer_impl.cpp、src/mln/text/placement.cpp、include/mln/renderer/render_segment.hpp、src/mln/renderer/retained_underlay_cache.cpp 和 particle_fast_path.hpp 在上述三个提交完全相同。此范围内 main 的分析适用于 V23;不能反推 STEP-109 优化已在 V23 APK 生效。

NavSea 输入冻结事实:Android 13 FZG1-2 V23,R0 [0,31) RETAINED 含地名 Symbol,C1 后续 82 层 CONTINUOUS。15.24 秒期间 placementChanged=true、R0 placementGatePassed=false、build/reuse=0、fallback/invalidation=453、fastPathHit Δ0、fastPathMiss Δ452,CPU 约 99%。快照只能证实 Gate 未通过,不能证实 C1 引发 R0 Symbol 实际位置变化。

2. Q1:Placement 产生、传播、状态语义

  1. src/mln/renderer/renderer.cpp:53-65:先尝试 tryRenderParticleFastFrame;未命中即创建 RenderTree、prepare、完整 render,再缓存 prepared tree。
  2. src/mln/renderer/render_orchestrator.cpp:737-749:逐个 RenderLayer.prepare,依据动态 needsPlacement() 构建 layersNeedPlacement。该集合与 R0 的结构性 placementCapable 不是一回事。
  3. 同文件 :758-773:反向遍历整个 RenderTree 的需要 Placement 的图层,CrossTileSymbolIndex.addLayer 汇聚 Tile/Symbol 变化。该过程是整图范围,不按 Segment 独立产生。
  4. 同文件 :775-810:在 Continuous 模式,placementChanged = !placementController.placementIsRecent(now, zoom, override)。为 true 才调用 Placement::create、placeLayers、setPlacement;否则 setPlacementStale。随后取全局 symbolFadeChange,调用 hasTransitions 控制 needsRepaint。它表示本轮要重新 Placement,不表示 R0 最终视觉结果必然不同;为 false 也不保证新旧 R0 内容相等。
  5. 同文件 :811-823:非 Continuous 模式还有每次非空 Symbol Layers 就重算的特殊路径,不应混同天气播放运行态。
  6. 同文件 :840-848:全局 Placement、layersNeedPlacement、symbolBucketsChanged 进入 RenderTree。Renderer 再独立判断各 Retained Segment。

src/mln/text/placement.cpp:45-59 CollisionGroups::get 在 crossSourceCollisions=false 时为不同 Source 创建隔离 collision group;开启时跨 Source Symbol 可以互相影响。Source 不是 Segment,同 Source 跨段仍存在相互影响,反之分段并不天然保证视觉隔离。

src/mln/text/placement.cpp:688-748 根据新旧 placed icon/text 更新 opacity 与 fadeStartTime;:1282、:1319 提供 symbolFadeChange / hasTransitions。shaders/symbol_sdf.vertex.glsl:153-158 和 shaders/symbol_text_and_icon.vertex.glsl:153-158 表明 alpha 依赖 symbol fade uniform。即使符号几何未变,透明度也可能变化。symbolFadeChange、placementTransitionActive 是全局时间/过渡量,不是 R0 独立验收结果。

Source layout/tile/visibility、Camera、Animation、Transition 均可能令 RenderTree prepare 或 Placement 状态变化;普通非 Symbol 粒子/颜色动画可以触发 repaint,但不能单靠 repaint 来源判定 R0 新旧 placed 结果。

3. Q2:R0 Cache Gate 的真实数据流

src/mln/renderer/renderer_impl.cpp:634-686 对每个 RETAINED Segment 分别取得:

  • retainedRangeContentFingerprint / layerContentChanged;
  • retainedSegmentSourceState 的 ready、stable、revision;
  • 组合的 segmentContentRevision;
  • retainedSegmentIsStable;
  • retainedRangeHasPlacementCapableLayer 结构性 placementCapable;
  • renderSegmentPlacementGatePassed 的结果。

确切 Gate:include/mln/renderer/render_segment.hpp:85-90 规定:无 Placement 能力的段直接放行 Placement Gate;否则必须同时满足 placementChanged=false、symbolFadeChange=0、placementTransitionActive=false。它读取的后三者是全局状态,不读取“事件由 C1 发起”或 R0 实际视觉等价证明。

src/mln/renderer/renderer_impl.cpp:661-686:Cache 支持还需要 Style/Sprite/global resources ready、Continuous 模式、没有 3D/debug、segmentStable、无 legacy/unsupported layer。Gate 失败可选择 static-placement 并传递给 cache.evaluate;:681-692 只有 Build/Reuse 才申请资源。:998-1028 先判断 global-live fallback,否则以 visitRenderSegmentExecution 顺序执行每段 composite/live,保持层序;actualExecution=live 不是证明 R0 Symbol 真变化,只表示本帧未从缓存 Composite。

src/mln/renderer/renderer_impl.cpp:590-630 还保留聚合的 retainedPlacementStable 和 Fast Path 支持判定,说明每段 Gate 局部化不等于整个 Fast Path 可局部化。

src/mln/renderer/retained_underlay_cache.cpp:85-105 将 camera/boundary、topology、content、tile、static-transition、static-placement 分别归类;:359-379 的 invalidate 更新 lastInvalidationReason,releaseResources 释放缓存,没有“每帧清零历史原因”的语义。因此早期快照当前 placementChanged=false 同时保留 lastInvalidationReason=static-placement 不是直接矛盾证据;必须在同一帧比较 Gate、cacheSupported、decision、lastReason 的采样时序。

STEP-102/STEP-106 的作用边界是 placement-free Retained 的局部复用。本机原始 Android 保存日志 artifacts/maplibre-particle/step106-t2-fix/runtime-T2/stream.log 末尾 tick130:placementChanged=true、crossSourceCollisions=true,但两个 placementCapable=false 的 Retained 段显示 actual=composite、decision=reuse、build=1、reuse=3878;内部 Annotation Symbol 被放入后续 Continuous。T1 的 Symbol-bearing Retained 未解除保守回退。历史验收未证明含地名 Symbol 的 R0 可复用。

4. Q3:七种反事实场景

场景 Placement 事件来源能否定位 R0 视觉是否可独立证明不变 Reuse 必要安全条件 V23 结论
1 R0 地名静态,C1 只更新非 Symbol 粒子/颜色 可按 Layer/Repaint 来源条件性追踪,Placement Controller 仍是全局 不能凭 C1 非 Symbol 就证明 R0 Tile/layout/content/Camera、全部 Symbol placed/opacity/fade 与缓存时等价,无其他全局影响 无充分逐段证明;fail-closed
2 C1 Symbol 改动,不同 Source、crossSourceCollisions 关闭 Source mutation 可区分 隔离碰撞降低风险,但仍不能独立证明 R0 Fade/Camera/其他 Source 状态不变 确认 Source 独立、Collision group 隔离、R0 自身配置、Tile、Fade 和层序均稳定 仅全局 Gate;fail-closed
3 C1 Symbol 改动且跨源碰撞开启 可以部分追踪事件 Source 不能;跨 Source Symbol 可能排斥 R0 地名 比较新旧全局 Collision 后 R0 每个文本/图标位置、显隐、Opacity、层序 无独立证明;fail-closed
4 R0 Symbol layout/tile/visibility/zoom 改动 局部 revision/fingerprint 可见 默认不能 重新 Placement / Build 缓存后验证 R0 版本和绘制结果 失效正确;fail-closed
5 Camera pan/zoom/rotation/pitch 改动 Camera/signature/matrix 可识别 旧屏幕纹理通常不等价 新 Camera、projection、viewport 对应重新构建缓存并核对碰撞 Camera/Boundary Gate;fail-closed
6 Fade/Placement Transition 活跃或刚结束 Controller 可观察过渡 不能由几何稳定推出 opacity 稳定 所有 R0 Symbol opacity/fade、Transition 与缓存时等价,且状态稳定 只有全局 Fade/Transition Gate;fail-closed
7 C1 自动插入 Annotation Symbol 验证场景的 Annotation layer 可追踪;NavSea 实际归段需运行证据 不能:跨段 Annotation 可能参与全局 Collision 明确真实层序、Source/collision 关系,证明 R0 新旧 placed/fade 等价 STEP-106 只覆盖 placement-free 段;fail-closed

重点:不同 Segment 不是 Collision 独立证据。仅识别“谁引起 Placement”并不足以让 R0 安全 Reuse;必须满足实际视觉、相机、图层顺序以及已缓存纹理的等价性。

5. Q4:Fast Path 的其他独立必要 Gate

Renderer 的 Fast Path 在完整 RenderTree 之前尝试。src/mln/renderer/renderer_impl.cpp:590-630 的 Retained eligibility/单 C 支持是前置条件;src/mln/renderer/particle_fast_path.hpp 的 Fast Path 判定还检查 boundary、topology、staticStable、hasPlacementTransition、pending changes、nonparticle transition/repaint、fading tiles、是否 Particle repaint、still image、continuous mode、debug、unsupported continuous layer、supported scene、annotation mutation 等。

因此R0 cache 有效是相关 Fast Path 的必要条件之一,绝不是充分条件。当前 cache-invalid 说明首先在此受阻,不允许宣称修好 R0 就一定 hit,也不能在本 STEP 扩大调查到 C1 82 层所有性能开销或实施多 Continuous 优化。

6. 数据流 / 状态流

Host 更新 / Style / Source / Camera / 动画 → Renderer::update 尝试 Fast Path(未命中时) → RenderOrchestrator.createRenderTree → 全局 layer.prepare / needsPlacement / CrossTileIndex / PlacementController → 全局 placementChanged / symbolFadeChange / hasTransitions → 对各 Retained 段独立:Source ready/stable/revision + Fingerprint + structural placementCapable → placement-free 段绕开全局 Placement Gate;Symbol-bearing 段使用全局三项严格 Gate → Style/Resource/Camera/Revision 安全校验 → Cache Build / Reuse / Fallback → 保序 RenderSegment Composite / Live → Fast Path 另有单 C、pending、unsupported continuous 等 Gate。

7. 确定事实、假设、证据不足

确定事实 不能直接断言的假设 需要补的证据
V23 核心 Placement/Cache 逻辑与当前 main 等价 C1 变化直接导致 R0 视觉改变 NavSea 同一帧事件来源、各 Source mutation、R0 全 Symbol 新旧结果
placementChanged 是全局计算触发量 placementChanged=true 意味 R0 所有地名改变 per-R0 placed/opacity/fade 结果签名
含 Symbol 的 R0 使用 global conservative Gate R0 453 次 fallback 是已证的性能 Bug 既有语义被违反的状态不变量;当前并无证据
T2 实验 placement-free 段重复复用 现有 T2 足以支持含 Symbol R0 NavSea 实际 Source/Annotation/跨源碰撞和真实设备对照
STEP-109 main 更改没有改 Gate V23 已有 STEP-109 修复 需下一正式发行 GSRL 和设备验收(本 Step 禁止)

8. 必选结论矩阵

维度 明确判定
Placement 事件来源能否定位 条件性可区分(Source/Layer 可追踪,global Placement 本身不标 per-Segment 责任)
能否证明 R0 视觉最终未变化 需扩展内部安全证据
现有 Gate 性质 正确的设计保守限制,不是已证明违反 STEP-102 语义的 Bug
缓存失效主要责任 多因素:Native Placement 缺少 per-R0 结果证据 + Native 全局 Gate,受 Collision 语义和 Host Style/Annotation 组合影响
下一步 Native 内部诊断补强 → 安全判据设计 → 另行授权真机对照;若改公共接口/Host,则交 Integration 协商

9. 依赖顺序与最小后续验证

  1. 在不影响生产安全和公开 API 的前提下,记录 placement 重算触发原因与时间,拆分 Source/Layer/Camera/Tile/transition 事件;
  2. 设计 per-R0 Placement 结果等价安全证据,至少包含 R0 Symbol placed IDs、位置、可见性、text/icon opacity/fade、Collision group、Tile/glyph/source 版本、Camera 和合成顺序;先审查其不变量;
  3. 只有确证 R0 不受 C1 影响时,才另立有边界的 Native Gate 优化任务;不能先去掉 placementChanged;
  4. 再调查 Fast Path 单 C / unsupported / pending 等 Gate;进行等条件 V23 与后续版本 CPU/Frame/Visual A-B;
  5. 如需要 NavSea 配合改 Style/Annotation 或改公共 Contract/API,形成 Integration 阻塞请求,不在 STEP-110 擅自实施。

调查状态:PARTIAL。 已完成静态因果审计、源码版本对照和 STEP-106 历史证据核对;缺少生产 R0 per-Symbol 等价、事件逐帧对应和 CPU 因果实测。这是安全证据边界,不是“R0 已修好”“缓存已命中”“CPU 已下降”。