Files
MapLibreParticleLab/docs/40_任务/STEP-100_SourceScoped_RenderSegment局部失效_Codex任务_v2.md
tei 524aaf4af5
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
fix: localize retained segment source invalidation
2026-10-06 17:18:09 +09:00

22 KiB
Raw Blame History

STEP-100 Source → RenderSegment 局部 Revision / Invalidation / Readiness 与独立 Fallback — Codex 任务 v2

0. 任务性质

这是 STEP-099 之后的正式修复任务。

STEP-099 已经完成纯诊断并 CLOSED / PASS,已确认 V17 的核心问题:

  1. 动态 GeoJSON Source/Tile mutation 会推进 global retained content revision;
  2. 即使 R0 不依赖该 GeoJSON,R0 retained cache 仍跟着 invalid/build;
  3. GeoJSON 更新期间约 200 ms 的 transient isLoaded=false 会通过 global RenderOrchestrator::isLoaded() 把整个 retained path 拉成 style-not-loaded;
  4. 当前 renderer 还存在 useRetainedRanges all-or-none gate:一个 RETAINED Segment fallback 时,会使其他本来可安全 reuse 的 RETAINED Segment 一起退出 retained path;
  5. STEP-099 diagnostics 已能正确给 source mutation 计算 affectedSegmentIndices,但该依赖关系仍只是诊断,不参与正式 invalidation。

本任务目标是把 Source/Tile 变化对 RETAINED Segment 的影响正式局部化。

本任务不是 V18 发布任务。Codex 只负责实现与源码级说明;Android/iOS 构建、APK、ADB、设备测试、最终 Git push、V18 release 由 RDC/ChatGPT 在 Codex 完成后独立执行。

未经用户明确要求,本任务 不加入、不运行 Jest。


1. 冻结基线

工作目录:

/Users/tei/dev/MapLibreParticleLab-step100

分支:

step100-segment-local-source-invalidation

开始基线必须重新核对,当前预期:

  • origin/particle/main
  • HEAD:c4b481fd2c5b5da20d31f6ae54011f0552d7dbd0
  • STEP-099 implementation:8a5214d92dd2f8899db76d2194da42999fa0c87d
  • V17 frozen tag:maplibre-particle/rn/13.4.1-particle.17
  • V17 frozen commit:e8b451fe70054107773e82484c80e90df3889147

禁止修改、移动、覆盖 V17 tag / release worktree。

STEP-100 是 V17 之后的新开发,最终只有 RDC 验收通过后才允许进入 V18 发布。

开始前必须读取:

  • 当前仓库实际 HEAD / status / diff;
  • 全局 Codex AGENTS;
  • ARCHITECTURE.md(若存在);
  • STEP-092 / 094 / 099 的任务、Step 记录、完成报告;
  • 当前:
    • renderer_impl.cpp
    • render_orchestrator.*
    • retained_underlay_cache.*
    • retained_underlay_runtime_state.hpp
    • render_segment.*
    • STEP-099 diagnostics 与 Android fixture;
  • 当前 RenderSegment / retained cache / particle fast path tests。

不得凭历史印象修改。


2. 最终架构原则

长期 RenderSegment 模型继续保持通用模型:

Style Z-order
  ↓
R / C / R / C / R / ...

其中:

  • R = RETAINED
  • C = CONTINUOUS

本任务只用 R-C-R 做最小验证,不把 R-C-R 固化成最终架构。

必须保持:

  1. Style layer 顺序是唯一 Z-order 真相;
  2. Renderer 不理解 AIS / Route / Weather / GeoJSON 等业务名;
  3. Source → Segment 依赖由当前实际 Style / RenderLayer / Source 关系推导;
  4. 一个 Source 可依赖多个 Segment;
  5. source-less layer 不制造假 dependency;
  6. unknown/global dependency 必须 fail closed;
  7. 相邻 Segment、多个 C、多个 R 的通用模型不能被破坏;
  8. 本任务不实现 multi-continuous Fast Path。

3. 本任务必须实现的核心目标

3.1 Source/Tile mutation 局部化

假设:

R0 depends [staticSource]
C  depends [particleField]
R1 depends [dynamicGeoJson]

当:

dynamicGeoJson mutation

正式 renderer 必须达到:

R0:
  content/revision 不因该 source mutation 改变
  cache 不 invalid
  cache 不 rebuild
  持续 reuse

R1:
  revision/invalidation 正常变化
  按需要 build/reuse/fallback

C:
  正常 continuous render

禁止继续出现:

dynamicGeoJson mutation
  ↓
global revision 变化
  ↓
R0 + R1 一起 rebuild

3.2 保留 global revision 兼容信号

STEP-100 不要求删除现有 global retainedUnderlayContentRevision。

为了保持:

  • diagnostics;
  • 现有 fast-path correctness;
  • 旧 API / compatibility;
  • conservative fallback;

允许并推荐继续维护 global revision。

但 RETAINED Segment cache 的正式内容 revision / invalidation 不能再只依赖这个 global source/tile revision。

当前 retainedUnderlayRevisionTracker 同时消费 global content revision 与 renderTreeParameters.loaded。STEP-100 必须明确拆成两种用途:

global retained revision / tracker
    -> compatibility / Fast Path / diagnostics

segment-local retained content state
    -> full CPU renderer 的每个 RETAINED Segment Build / Reuse / Fallback

Segment-local cache content state 禁止继续直接消费:

  • global source/tile content revision;
  • global renderTreeParameters.loaded。

否则即使 source-scoped revision 已实现,R0 仍会被无关 GeoJSON transient unloaded 或 global revision 连带失效。

允许 global revision / global tracker继续按原逻辑变化;禁止为了让测试 PASS 而停止 global revision 增长或放宽 Fast Path。

3.3 per-RETAINED-Segment readiness

不得修改任何 Source isLoaded() 定义。

不得修改:

renderTreeParameters->loaded = updateParameters->styleLoaded && isLoaded();

这个 global loaded 仍作为 MapLibre 现有全局兼容事实。

但 retained segment 的 cache Build/Reuse/Fallback 必须有自己的安全 readiness 判断。

最低语义:

  • Style 本身未 ready:fail closed;
  • image manager / sprite / glyph 等无法安全定位到单 Segment 的全局前置条件未 ready:fail closed;
  • Segment 已知依赖的 source 中任一缺失或 isLoaded()==false:仅该 Segment fail closed;
  • Segment 已知依赖的 source hasFadingTiles()==true:仅该 Segment视为 unstable;
  • 不被该 Segment 引用的 source unloaded 或 fading,不得让该 Segment cache fallback/rebuild;
  • retainedRangeIsStable(begin,end) 的 source fading 检查必须按该 range 的真实 source dependency 缩小,不能继续遍历全部 renderSources 作为 full-CPU retained gate;
  • Fast Path 原有 global fading gate 保持不变;
  • source ID 无法解析、dependency 缺失或真正无法证明安全时 fail closed。

必须区分:

  1. 已知 Source dependency;
  2. symbol/global prerequisite(glyph/image/placement 等);
  3. 真正 unknown dependency。

STEP-099 的 unknownDependencies 目前会把 symbol 相关条件标为 unknown。STEP-100 不得机械地把“存在 symbol”解释成永久 fallback;symbol 所需 glyph/image/placement 应作为明确的 global prerequisite/fail-closed 条件处理。

因此 T3 中一个:

no layer
enabled=false
isLoaded=false

的 RasterArray source 可以继续让 global RenderOrchestrator::isLoaded()==false,但不得因此让与它无依赖关系的 R0/R1 cache 全部 style-not-loaded。

本 Step 的运行态硬验收范围是 MapMode::Continuous(粒子正式实时渲染模式)。当前非 Continuous / still 路径如果在 global unloaded 时提前返回,本 Step 不强行改变其兼容语义;完成报告必须明确该适用范围。

3.4 RETAINED Segment 独立决策

当前 V17 路径存在类似:

useRetainedRanges = all retained segments supported

的 all-or-none 语义。

STEP-100 必须改为每个 RETAINED Segment 独立拥有:

  • readiness/support decision;
  • Build / Reuse / Fallback;
  • content/source revision;
  • invalidation reason。

如果:

R0 = Reuse
R1 = Fallback

则本帧必须允许:

R0 composite cached texture
C  live render
R1 live render fallback

而不是退化为“整个 Style 全部 live render”。

如果某 retained cache 自身 build 失败,应优先只 fallback 该 Segment;只有无法证明局部安全的 context/global resource failure 才允许全 retained fail closed。


4. Source-scoped revision 设计要求

Codex 必须基于当前代码选择最小、稳定设计,并在完成报告说明实际方案。

推荐方向但不强制具体类名:

  • 为 Source/Tile mutation 维护 source-scoped monotonic revision;
  • keyed by stable sourceId,但必须处理 source 删除后同 ID recreate,不能让新 source 错误继承可复用的旧 cache identity;可用 generation/epoch 或保留单调 tombstone 语义实现;
  • source diff、feature-state mutation、tile changed 等所有当前会进入 Source/Tile retained content invalidation 的正式路径都必须覆盖;
  • 能定位 sourceId 的 mutation 只推进对应 source revision;无法定位 sourceId 的 mutation继续 global fail closed;
  • Segment content revision 由:
    • 该 Segment 的 dependent source revisions;
    • 当前 Segment layer fingerprint / layer mutation;
    • 必须全局影响 retained 的 global revision 类别;
    • topology / camera / viewport 等既有安全条件 共同决定。

Source/Tile mutation 不应推进“所有 Segment 都消费的 retained cache revision”。

必须避免:

  • 用屏幕位置猜 dependency;
  • 在 renderer 中硬编码 source/layer ID;
  • 从 STEP-099 diagnostics JSON 反向驱动正式 renderer;
  • 建立 diagnostics 与 production 两套互相漂移的 dependency 真相。

如果需要提取 production-grade dependency helper,应让 diagnostics 和正式逻辑共享同一只读推导源。正式 renderer 不得从 diagnostics JSON 或 RetainedUnderlayRuntimeState 反向取 dependency。

Source-scoped revision 属正式 correctness state,不得沿用 STEP-099 diagnostics 的“best-effort 记录失败可忽略”语义。正式 revision/dependency state 若无法可靠更新,必须 conservative global invalidation/fail closed。


5. Global mutation 仍可保守处理

本任务只要求 Source/Tile mutation 与明确 Source readiness 局部化。

以下无法安全局部化的事件,本轮允许继续 invalidate/fail closed 全 retained:

  • context reset;
  • style reload / topology change;
  • image mutation;
  • glyph mutation;
  • sprite/image manager 全局 readiness;
  • placement / symbol fade / glyph/image/sprite 全局状态;
  • camera / transform / viewport / resize / pixel ratio;
  • debug mode;
  • 3D / unsupported / legacy ordering;
  • 无法定位 dependency 的 mutation;
  • 真正 unknown/global dependency。

注意:Source 自身的 loaded / fading 不属于这里的 global fail-closed 项;若 source dependency 可定位,必须按 Segment 局部判断。Fast Path 的 global source/fading gate除外,本轮保持原状。

完成报告必须列出:

  • 已局部化的 mutation 类型;
  • 仍为 global 的 mutation 类型;
  • 为什么保守处理。

6. Fast Path 本轮严格不改

STEP-099 已确认 Fast Path 仍有 global gates:

  • global update signature(sources/layers/images 等);
  • global content revision;
  • global fade;
  • pendingChanges;
  • placement/light/non-particle transition/repaint;
  • annotation mutation;
  • topology/boundary/revision/cache validity;
  • singleContinuousSegment。

STEP-100 禁止修改这些 gate 的语义。

允许:

  • Source mutation 后 Fast Path miss;
  • transient source readiness 时 Fast Path fail closed;
  • full CPU renderer 被调用。

但进入 full CPU renderer 后,必须能做到:

R0 retained cache reuse
R1 rebuild/fallback

这正是本任务验收目标。

Multi-continuous Fast Path 明确不在本任务。


7. Render 顺序与混合 Build/Reuse/Fallback

实现 per-segment mixed decisions 时必须保持原 MapLibre 绘制语义:

  • Style Z-order;
  • global layerIndex;
  • opaque reverse traversal;
  • translucent forward traversal;
  • currentLayer;
  • depthRangeSize;
  • legacy item 不双绘;
  • debug overlay 不重复;
  • retained texture composite 不改变 segment 顺序。

推荐的逻辑行为:

for segment in Style order:
    RETAINED + Build:
        build cache
        composite cache
    RETAINED + Reuse:
        composite cache
    RETAINED + Fallback:
        live render only this range
    CONTINUOUS:
        live render this range

实际代码可按现有 RenderPass 安全实现,不要求机械照搬伪代码。

必须特别检查:

  • 一个 R fallback 不得导致另一个 R live redraw;
  • resource prepare 在 Build 前失败时,必须先把该 Segment 本帧 decision 改为 Fallback,再进入主 buffer 绘制;
  • retained build 中途失败时,必须 invalidate/标记该 Segment失败并把该 Segment 本帧 decision 改为 Fallback,禁止 composite 旧纹理;
  • 如果失败意味着 context / shared GPU state 已不可信,允许全局 fail closed;
  • fallback range 的 legacy items 只绘一次,不得因 per-segment 主循环重复 render;
  • debug overlay 全帧只绘一次;
  • Segment 间 Opaque/Translucent 语义不得被破坏;
  • 相邻 R、多个 C、透明叠加与 legacy scene 不能因 mixed execution 出现漏绘/双绘。

推荐执行顺序:先完成各 Segment resource prepare 与必要离屏 Build;再 clear 主 buffer;最后按 Style Segment 顺序逐段选择 cached composite 或 live range render。


8. Diagnostics 必须继续可证明局部性

保留 STEP-099 diagnostics,并 additive 增加足够字段,使 RDC 能验证正式行为。

每个 RETAINED Segment 至少应能观察:

  • dependentSourceIds;
  • segment readiness / support;
  • segment-local source/content revision 或等价可证明值;
  • Build / Reuse / Fallback 的最终本帧 execution decision;
  • build/reuse/fallback/invalidation count;
  • last invalidation/fallback reason。

Source mutation 继续输出:

  • sourceId;
  • sequence/reason/action;
  • affectedSegmentIndices。

要求:

step099-dynamic mutation
affected=[R1]
R0 revision unchanged
R0 cache reuse++
R0 build unchanged
R0 invalidation unchanged

Diagnostics 仍是 pull/event low-frequency,不允许逐帧高频 spam。


9. Android Test App

复用 STEP-099,不另建独立 App。

至少保留并完善:

T1 STATIC

验证静态 R-C-R:

  • loaded settle;
  • R0/R1 build 一次后稳定 reuse;
  • 不产生持续 rebuild。

T2 GEOJSON_1HZ

两个 GeoJSON source 交错,各约 1 Hz。

重点:

  • global loaded transient false window 可以继续存在;
  • 只引用某 source 的 retained Segment 才允许因该 source mutation 改变;
  • 无关 retained Segment build/invalidation 不得跟随增长。

T3 PERMANENT_UNREADY

保留一个:

  • no layer;
  • enabled=false;
  • isLoaded=false;
  • no field submission

的 RasterArray source。

预期:

  • global renderOrchestratorLoaded 仍可为 false;
  • 但无关 R0/R1 retained cache 不应因它持续 style-not-loaded fallback;
  • 能安全判断依赖的 retained Segment应继续 build/reuse。

T4 R-C-R(主验收)

必须保持清晰依赖:

R0 -> dedicated static source only
C  -> WeatherColor/Particle field
R1 -> dedicated dynamic GeoJSON source

运行动态 GeoJSON 约 1 Hz。

必须调整 fixture,避免 STEP-099 中 R1 再复用 R0 的 static source:

  • R0 static source 只出现在 R0;
  • R1 dynamic source 只出现在 R1;
  • 若 MapLibre 内建 annotation source/layer 自动进入 R1,必须在 diagnostics 中列明,并保证验收窗口内 annotation 不发生 mutation;
  • 不允许用业务 ID 特判 renderer。

RDC 验收区间必须支持“settled baseline → 60 s pure dynamic-source mutation window → final”对比。每个 aggregate sample 同时记录 mutation source/sequence、各 Segment local revision、readiness、最终 execution decision 与 cache counters。


10. STEP-100 硬验收标准

A. R0 locality

T4 settle 后记录 baseline。

随后至少 60 s dynamic GeoJSON:

  • R0 buildDelta == 0
  • R0 invalidationDelta == 0
  • R0 reuseDelta > 0
  • R0 segment-local revision/content state 不因 R1 dynamic source mutation 改变
  • 即使某采样点 global renderOrchestratorLoaded=false,只要 R0 自身 prerequisites/dependencies ready,R0 final decision 仍可为 Reuse

如果为了安全出现明确 global event,应在日志中可解释并重新选择纯动态 mutation 区间;不能把 global event 混入后声称 PASS。

B. R1 reacts

同一时间窗:

  • R1 必须能观察到 source mutation;
  • R1 revision / build / invalidation / fallback 中至少有与动态 source 对应的实际变化;
  • affectedSegmentIndices 必须只包含真实依赖 Segment。

C. global revision compatibility

  • global retainedUnderlayContentRevision 允许并预期继续增长;
  • 不能通过停止 global revision 增长来制造 R0 PASS。

D. readiness locality

T2 transient unloaded 或 T3 permanent unrelated unloaded 时:

  • global loaded 可 false;
  • R0 不得仅因无关 source 的 unloaded 而 invalid/rebuild/fallback;
  • R0 cache 必须继续可复用。

E. correctness

  • 无 crash / ANR;
  • 无 stale cache composite;
  • 无 Segment Z-order 错乱;
  • 无 fallback range 双绘;
  • Cache OFF 仍安全走 live renderer;
  • plan invalid 仍 fail closed。

任一硬门槛失败,STEP-100 不得宣布 PASS,不得进入 V18。


11. Native 非 Jest 测试要求

Codex 必须新增或扩展现有 C++ 测试源码,至少覆盖:

  1. source A mutation 只改变依赖 A 的 Segment revision;
  2. source A 被多个 Segment 共享时,对应多个 Segment 都变化;
  3. unrelated Segment revision 不变;
  4. source-less Segment 不制造 dependency;
  5. unknown/global dependency fail closed;
  6. unrelated source unloaded 不使安全 Segment readiness false;
  7. dependent source unloaded 使对应 Segment readiness false;
  8. R0 Reuse + R1 Fallback 的 mixed decision;
  9. 单 Segment build failure 不 composite stale cache;
  10. query diagnostics 不改变 revision/cache state;
  11. global non-source invalidation 仍影响所有 retained;
  12. source 删除后同 ID recreate 不误复用旧 Segment cache identity;
  13. dependent source fading 只影响对应 Segment;unrelated source fading 不影响安全 Segment;
  14. Cache OFF / invalid plan 行为不回归;
  15. 至少有一组能证明实际 mixed render 没有漏绘/双绘的 renderer-level draw-count、pixel 或现有等价测试入口;不能只用 decision unit test 代替。

不得修改测试断言去适应错误实现。


12. 允许修改范围

允许:

  • RenderOrchestrator source mutation/readiness bookkeeping;
  • RenderSegment dependency production helper;
  • Renderer retained per-segment revision/readiness/decision;
  • RetainedUnderlayCache/RuntimeState 为局部状态增加必要字段;
  • additive diagnostics JSON/TS;
  • Android Validation Test App STEP-099 fixture/log;
  • 相关 Native C++ tests;
  • STEP-100 Step 记录/完成报告/开发记录。

禁止:

  • NavSea;
  • Integration;
  • V17 tag/release worktree;
  • Source isLoaded() 定义;
  • Particle field/provider/network;
  • Fast Path gate semantic;
  • singleContinuousSegment;
  • multi-continuous Fast Path;
  • 自动改变 Style Z-order;
  • provider/business-specific source/layer ID;
  • unrelated cleanup/refactor;
  • Jest。

13. Codex 执行边界

Codex 本轮负责:

  1. 读取实际源码和任务书;
  2. 实现代码;
  3. 编写 Native 非 Jest 测试源码;
  4. 更新 Android Test App 验证入口;
  5. 执行轻量静态检查,如 git diff --check;
  6. 生成中文完成报告。

Codex 不得执行:

  • Android Gradle / APK;
  • ADB / 安装 / 实机;
  • Xcode / iOS device;
  • V18 release;
  • tag;
  • push;
  • Jest。

本轮 Codex 完成报告固定:

docs/报告/STEP-100_SourceScoped_RenderSegment局部失效_Codex完成报告_v1.md

Step 记录固定:

docs/50_Step记录/STEP-100_SourceScoped_RenderSegment局部失效_v1.md

完成报告必须写清:

  • 实际 Codex model / reasoning effort;
  • start HEAD;
  • 修改文件;
  • 生产设计;
  • source revision/readiness 规则;
  • mixed retained decision 规则;
  • Fast Path 明确未改;
  • 测试源码新增情况;
  • 实际执行 / NOT RUN;
  • 风险与 RDC 验收点;
  • 未 commit / 未 push(如果确实如此)。

Codex 不 commit、不 push。由 RDC 验证通过后统一 Git 收尾。


14. RDC / ChatGPT 后续独立验收

Codex 返回后由 RDC 负责:

  1. source/diff 审计;
  2. Native Mac 编译;
  3. Native focused + broad related C++ tests;
  4. Android x86_64 OpenGL validation APK 单次构建;
  5. Mac ADB 直连 FZG1-2;
  6. 自动跑 T1/T2/T3/T4;
  7. 解析 R0/R1 build/reuse/invalidation/revision/readiness;
  8. 确认硬验收标准 A~E;
  9. 若失败,回到 STEP-100 修复,不发布;
  10. 若全部 PASS,完成 STEP-100 Git push;
  11. 再按现有统一发行流程升级到: maplibre-particle/rn/13.4.1-particle.18
  12. V18 build/release/public artifact 验证通过后才宣布 V18 发布完成。

15. 停止条件

Codex 遇到以下任一情况必须停止并报告,不得扩大范围:

  • 要修改 Source isLoaded() 才能继续;
  • 需要放宽 Fast Path gate;
  • 必须改变 singleContinuous / multi-continuous;
  • production dependency 无法可靠从现有 Style/RenderLayer/Source 推导;
  • per-segment fallback 会破坏 RenderPass/Z-order 且无法在当前范围安全解决;
  • 发现需要 NavSea/Integration 配合;
  • 需要业务 layer/source ID;
  • 需要改变 V17 release。

16. 本任务最终原则

STEP-100 的目标不是让“全局世界看起来 loaded”。

目标是:

即使某个 GeoJSON Source 正在变化或短暂 unready,只要 R0 与它没有依赖关系,R0 retained cache 就必须继续安全 reuse,不能被迫一起刷新。

Fast Path 是否还能 miss,是后续独立问题。

只有这一点经过 Native + Android R-C-R 实测成立,才允许进入 V18 发布。